summaryrefslogtreecommitdiff
path: root/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
diff options
context:
space:
mode:
authorAndrzej Hajda <a.hajda@samsung.com>2012-11-22 11:39:18 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2013-02-05 17:39:04 -0200
commitcac47f1822fcb97018e24b05a7fb31f11a6bc28c (patch)
tree12f68b61d933c8885354b388244cce9136f571f6 /drivers/media/i2c/s5c73m3/s5c73m3-spi.c
parent6e83e6e25eb49dc57a69b3f8ecc1e764c9775101 (diff)
downloadlwn-cac47f1822fcb97018e24b05a7fb31f11a6bc28c.tar.gz
lwn-cac47f1822fcb97018e24b05a7fb31f11a6bc28c.zip
[media] V4L: Add S5C73M3 camera driver
Add driver for S5C73M3 image sensor. The driver exposes the sensor as two subdevs: pure sensor and output interface. Two subdev architecture supports interleaved UYVY/JPEG image format with separate frame size for both sub-formats, there is a spearate pad for each sub-format. Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/i2c/s5c73m3/s5c73m3-spi.c')
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-spi.c156
1 files changed, 156 insertions, 0 deletions
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
new file mode 100644
index 000000000000..889139c2ac5e
--- /dev/null
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
@@ -0,0 +1,156 @@
+/*
+ * Samsung LSI S5C73M3 8M pixel camera driver
+ *
+ * Copyright (C) 2012, Samsung Electronics, Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * 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/sizes.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/media.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include "s5c73m3.h"
+
+#define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI"
+
+enum spi_direction {
+ SPI_DIR_RX,
+ SPI_DIR_TX
+};
+
+static int spi_xmit(struct spi_device *spi_dev, void *addr, const int len,
+ enum spi_direction dir)
+{
+ struct spi_message msg;
+ int r;
+ struct spi_transfer xfer = {
+ .len = len,
+ };
+
+ if (dir == SPI_DIR_TX)
+ xfer.tx_buf = addr;
+ else
+ xfer.rx_buf = addr;
+
+ if (spi_dev == NULL) {
+ dev_err(&spi_dev->dev, "SPI device is uninitialized\n");
+ return -ENODEV;
+ }
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ r = spi_sync(spi_dev, &msg);
+ if (r < 0)
+ dev_err(&spi_dev->dev, "%s spi_sync failed %d\n", __func__, r);
+
+ return r;
+}
+
+int s5c73m3_spi_write(struct s5c73m3 *state, const void *addr,
+ const unsigned int len, const unsigned int tx_size)
+{
+ struct spi_device *spi_dev = state->spi_dev;
+ u32 count = len / tx_size;
+ u32 extra = len % tx_size;
+ unsigned int i, j = 0;
+ u8 padding[32];
+ int r = 0;
+
+ memset(padding, 0, sizeof(padding));
+
+ for (i = 0; i < count ; i++) {
+ r = spi_xmit(spi_dev, (void *)addr + j, tx_size, SPI_DIR_TX);
+ if (r < 0)
+ return r;
+ j += tx_size;
+ }
+
+ if (extra > 0) {
+ r = spi_xmit(spi_dev, (void *)addr + j, extra, SPI_DIR_TX);
+ if (r < 0)
+ return r;
+ }
+
+ return spi_xmit(spi_dev, padding, sizeof(padding), SPI_DIR_TX);
+}
+
+int s5c73m3_spi_read(struct s5c73m3 *state, void *addr,
+ const unsigned int len, const unsigned int tx_size)
+{
+ struct spi_device *spi_dev = state->spi_dev;
+ u32 count = len / tx_size;
+ u32 extra = len % tx_size;
+ unsigned int i, j = 0;
+ int r = 0;
+
+ for (i = 0; i < count ; i++) {
+ r = spi_xmit(spi_dev, addr + j, tx_size, SPI_DIR_RX);
+ if (r < 0)
+ return r;
+ j += tx_size;
+ }
+
+ if (extra > 0)
+ return spi_xmit(spi_dev, addr + j, extra, SPI_DIR_RX);
+
+ return 0;
+}
+
+static int __devinit s5c73m3_spi_probe(struct spi_device *spi)
+{
+ int r;
+ struct s5c73m3 *state = container_of(spi->dev.driver, struct s5c73m3,
+ spidrv.driver);
+ spi->bits_per_word = 32;
+
+ r = spi_setup(spi);
+ if (r < 0) {
+ dev_err(&spi->dev, "spi_setup() failed\n");
+ return r;
+ }
+
+ mutex_lock(&state->lock);
+ state->spi_dev = spi;
+ mutex_unlock(&state->lock);
+
+ v4l2_info(&state->sensor_sd, "S5C73M3 SPI probed successfully\n");
+ return 0;
+}
+
+static int __devexit s5c73m3_spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+int s5c73m3_register_spi_driver(struct s5c73m3 *state)
+{
+ struct spi_driver *spidrv = &state->spidrv;
+
+ spidrv->remove = __devexit_p(s5c73m3_spi_remove);
+ spidrv->probe = s5c73m3_spi_probe;
+ spidrv->driver.name = S5C73M3_SPI_DRV_NAME;
+ spidrv->driver.bus = &spi_bus_type;
+ spidrv->driver.owner = THIS_MODULE;
+
+ return spi_register_driver(spidrv);
+}
+
+void s5c73m3_unregister_spi_driver(struct s5c73m3 *state)
+{
+ spi_unregister_driver(&state->spidrv);
+}