android_kernel_modules_leno.../camera/drivers/media/i2c/m10mo_spi.c

231 lines
5.2 KiB
C

/*
* Copyright (c) 2014 Intel Corporation. All Rights Reserved.
*
* Partially based on m-5mols kernel driver,
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
*
* Partially based on jc_v4l2 kernel driver from http://opensource.samsung.com
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/atomisp_platform.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <media/v4l2-device.h>
#include <media/m10mo_atomisp.h>
#include "m10mo.h"
static inline int spi_xmit(struct spi_device *spi, const u8 *addr, const int len)
{
int ret;
struct spi_message msg;
struct spi_transfer xfer = {
.len = len,
.tx_buf = addr,
.bits_per_word = 32,
};
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
ret = spi_sync(spi, &msg);
if (ret < 0)
dev_err(&spi->dev, "error %d\n", ret);
return ret;
}
static inline int spi_xmit_rx(struct spi_device *spi, u8 *in_buf, size_t len)
{
int ret;
u8 read_out_buf[2];
struct spi_message msg;
struct spi_transfer xfer = {
.tx_buf = read_out_buf,
.rx_buf = in_buf,
.len = len,
.cs_change = 0,
};
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
ret = spi_sync(spi, &msg);
if (ret < 0)
dev_err(&spi->dev, "%s - error %d\n", __func__, ret);
return ret;
}
/* TDB: Not tested */
int m10mo_spi_read(struct spi_device *spi, u8 *buf, size_t len,
const int rxSize)
{
int k;
int ret = 0;
u8 temp_buf[4] = {0};
u32 count = len / rxSize;
u32 extra = len % rxSize;
for (k = 0; k < count; k++) {
ret = spi_xmit_rx(spi, &buf[rxSize * k], rxSize);
if (ret < 0) {
dev_err(&spi->dev, "%s - error %d\n", __func__, ret);
return -EINVAL;
}
}
if (extra != 0) {
ret = spi_xmit_rx(spi, &buf[rxSize * k], extra);
if (ret < 0) {
dev_err(&spi->dev, "%s - error %d\n", __func__, ret);
return -EINVAL;
}
}
for (k = 0; k < len - 3; k += 4) {
memcpy(temp_buf, (char *)&buf[k], sizeof(temp_buf));
buf[k] = temp_buf[3];
buf[k+1] = temp_buf[2];
buf[k+2] = temp_buf[1];
buf[k+3] = temp_buf[0];
}
return 0;
}
int m10mo_spi_write(struct spi_device *spi, const u8 *addr,
const int len, const int txSize)
{
int i, j = 0;
int ret = 0;
u8 paddingData[8];
u32 count = len / txSize;
u32 extra = len % txSize;
dev_dbg(&spi->dev, "Entered to spi write with count = %d extra = %d\n",
count, extra);
for (i = 0 ; i < count ; i++) {
ret = spi_xmit(spi, &addr[j], txSize);
j += txSize;
if (ret < 0) {
dev_err(&spi->dev, "failed to write spi_xmit\n");
goto exit_err;
}
}
if (extra) {
ret = spi_xmit(spi, &addr[j], extra);
if (ret < 0) {
dev_err(&spi->dev, "failed to write spi_xmit\n");
goto exit_err;
}
}
for (i = 0; i < 4; i++) {
memset(paddingData, 0, sizeof(paddingData));
ret = spi_xmit(spi, paddingData, 8);
if (ret < 0) {
dev_err(&spi->dev, "failed to write spi_xmit\n");
goto exit_err;
}
}
dev_dbg(&spi->dev, "FW upload done!!\n");
exit_err:
return ret;
}
static int m10mo_spi_probe(struct spi_device *spi)
{
int ret = -ENODEV;
struct m10mo_spi *m10mo_spi_dev;
struct m10mo_atomisp_spi_platform_data *pdata;
dev_dbg(&spi->dev, "Probe M10MO SPI\n");
pdata = dev_get_platdata(&spi->dev);
if (!pdata) {
dev_err(&spi->dev, "Missing platform data. Can't continue");
return -ENODEV;
}
if (!pdata->device_data) {
dev_err(&spi->dev, "Missing link to m10mo main driver. Can't continue");
return -ENODEV;
}
m10mo_spi_dev = kzalloc(sizeof(struct m10mo_spi), GFP_KERNEL);
if (!m10mo_spi_dev) {
dev_err(&spi->dev, "Can't get memory\n");
return -ENOMEM;
}
if (spi_setup(spi)) {
dev_err(&spi->dev, "failed to setup spi for m10mo_spi\n");
ret = -EINVAL;
goto err_setup;
}
spi_set_drvdata(spi, m10mo_spi_dev);
m10mo_spi_dev->spi_device = spi;
m10mo_spi_dev->read = m10mo_spi_read;
m10mo_spi_dev->write = m10mo_spi_write;
m10mo_spi_dev->spi_enabled = pdata->spi_enabled;
m10mo_register_spi_fw_flash_interface(pdata->device_data,
m10mo_spi_dev);
dev_err(&spi->dev, "m10mo_spi successfully probed\n");
return 0;
err_setup:
kfree(m10mo_spi_dev);
return ret;
}
static int m10mo_spi_remove(struct spi_device *spi)
{
struct m10mo_spi *m10mo_spi_dev;
m10mo_spi_dev = spi_get_drvdata(spi);
kfree(m10mo_spi_dev);
return 0;
}
static const struct spi_device_id m10mo_spi_id_table[] = {
{ "m10mo_spi", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, m10mo_spi_id_table);
static struct spi_driver m10mo_spi_driver = {
.driver = {
.name = "m10mo_spi",
.owner = THIS_MODULE,
},
.probe = m10mo_spi_probe,
.remove = m10mo_spi_remove,
.id_table = m10mo_spi_id_table,
};
module_spi_driver(m10mo_spi_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Samu Onkalo <samu.onkalo@intel.com>");
MODULE_DESCRIPTION("m10mo spi interface driver");