Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

V4L/DVB: V4L2: soc-camera: add a MIPI CSI-2 driver for SH-Mobile platforms

Some SH-Mobile SoCs implement a MIPI CSI-2 controller, that can interface to
several video clients and send data to the CEU or to the Image Signal
Processor. This patch implements a v4l2-subdevice driver for CSI-2 to be used
within the soc-camera framework, implementing the second subdevice in addition
to the actual video clients.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

authored by

Guennadi Liakhovetski and committed by
Mauro Carvalho Chehab
077e2c10 52d268a3

+407
+6
drivers/media/video/Kconfig
··· 876 876 ---help--- 877 877 This is a v4l2 driver for the PXA27x Quick Capture Interface 878 878 879 + config VIDEO_SH_MOBILE_CSI2 880 + tristate "SuperH Mobile MIPI CSI-2 Interface driver" 881 + depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK 882 + ---help--- 883 + This is a v4l2 driver for the SuperH MIPI CSI-2 Interface 884 + 879 885 config VIDEO_SH_MOBILE_CEU 880 886 tristate "SuperH Mobile CEU Interface driver" 881 887 depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
+1
drivers/media/video/Makefile
··· 160 160 obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o 161 161 obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o 162 162 obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o 163 + obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o 163 164 obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o 164 165 165 166 obj-$(CONFIG_ARCH_DAVINCI) += davinci/
+354
drivers/media/video/sh_mobile_csi2.c
··· 1 + /* 2 + * Driver for the SH-Mobile MIPI CSI-2 unit 3 + * 4 + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + 11 + #include <linux/delay.h> 12 + #include <linux/i2c.h> 13 + #include <linux/io.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/pm_runtime.h> 16 + #include <linux/slab.h> 17 + #include <linux/videodev2.h> 18 + 19 + #include <media/sh_mobile_csi2.h> 20 + #include <media/soc_camera.h> 21 + #include <media/v4l2-common.h> 22 + #include <media/v4l2-dev.h> 23 + #include <media/v4l2-device.h> 24 + #include <media/v4l2-mediabus.h> 25 + #include <media/v4l2-subdev.h> 26 + 27 + #define SH_CSI2_TREF 0x00 28 + #define SH_CSI2_SRST 0x04 29 + #define SH_CSI2_PHYCNT 0x08 30 + #define SH_CSI2_CHKSUM 0x0C 31 + #define SH_CSI2_VCDT 0x10 32 + 33 + struct sh_csi2 { 34 + struct v4l2_subdev subdev; 35 + struct list_head list; 36 + struct notifier_block notifier; 37 + unsigned int irq; 38 + void __iomem *base; 39 + struct platform_device *pdev; 40 + struct sh_csi2_client_config *client; 41 + }; 42 + 43 + static int sh_csi2_try_fmt(struct v4l2_subdev *sd, 44 + struct v4l2_mbus_framefmt *mf) 45 + { 46 + struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); 47 + struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; 48 + 49 + if (mf->width > 8188) 50 + mf->width = 8188; 51 + else if (mf->width & 1) 52 + mf->width &= ~1; 53 + 54 + switch (pdata->type) { 55 + case SH_CSI2C: 56 + switch (mf->code) { 57 + case V4L2_MBUS_FMT_UYVY8_2X8: /* YUV422 */ 58 + case V4L2_MBUS_FMT_YUYV8_1_5X8: /* YUV420 */ 59 + case V4L2_MBUS_FMT_GREY8_1X8: /* RAW8 */ 60 + case V4L2_MBUS_FMT_SBGGR8_1X8: 61 + case V4L2_MBUS_FMT_SGRBG8_1X8: 62 + break; 63 + default: 64 + /* All MIPI CSI-2 devices must support one of primary formats */ 65 + mf->code = V4L2_MBUS_FMT_YUYV8_2X8; 66 + } 67 + break; 68 + case SH_CSI2I: 69 + switch (mf->code) { 70 + case V4L2_MBUS_FMT_GREY8_1X8: /* RAW8 */ 71 + case V4L2_MBUS_FMT_SBGGR8_1X8: 72 + case V4L2_MBUS_FMT_SGRBG8_1X8: 73 + case V4L2_MBUS_FMT_SBGGR10_1X10: /* RAW10 */ 74 + case V4L2_MBUS_FMT_SBGGR12_1X12: /* RAW12 */ 75 + break; 76 + default: 77 + /* All MIPI CSI-2 devices must support one of primary formats */ 78 + mf->code = V4L2_MBUS_FMT_SBGGR8_1X8; 79 + } 80 + break; 81 + } 82 + 83 + return 0; 84 + } 85 + 86 + /* 87 + * We have done our best in try_fmt to try and tell the sensor, which formats 88 + * we support. If now the configuration is unsuitable for us we can only 89 + * error out. 90 + */ 91 + static int sh_csi2_s_fmt(struct v4l2_subdev *sd, 92 + struct v4l2_mbus_framefmt *mf) 93 + { 94 + struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); 95 + u32 tmp = (priv->client->channel & 3) << 8; 96 + 97 + dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); 98 + if (mf->width > 8188 || mf->width & 1) 99 + return -EINVAL; 100 + 101 + switch (mf->code) { 102 + case V4L2_MBUS_FMT_UYVY8_2X8: 103 + tmp |= 0x1e; /* YUV422 8 bit */ 104 + break; 105 + case V4L2_MBUS_FMT_YUYV8_1_5X8: 106 + tmp |= 0x18; /* YUV420 8 bit */ 107 + break; 108 + case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE: 109 + tmp |= 0x21; /* RGB555 */ 110 + break; 111 + case V4L2_MBUS_FMT_RGB565_2X8_BE: 112 + tmp |= 0x22; /* RGB565 */ 113 + break; 114 + case V4L2_MBUS_FMT_GREY8_1X8: 115 + case V4L2_MBUS_FMT_SBGGR8_1X8: 116 + case V4L2_MBUS_FMT_SGRBG8_1X8: 117 + tmp |= 0x2a; /* RAW8 */ 118 + break; 119 + default: 120 + return -EINVAL; 121 + } 122 + 123 + iowrite32(tmp, priv->base + SH_CSI2_VCDT); 124 + 125 + return 0; 126 + } 127 + 128 + static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = { 129 + .s_mbus_fmt = sh_csi2_s_fmt, 130 + .try_mbus_fmt = sh_csi2_try_fmt, 131 + }; 132 + 133 + static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops; 134 + 135 + static struct v4l2_subdev_ops sh_csi2_subdev_ops = { 136 + .core = &sh_csi2_subdev_core_ops, 137 + .video = &sh_csi2_subdev_video_ops, 138 + }; 139 + 140 + static void sh_csi2_hwinit(struct sh_csi2 *priv) 141 + { 142 + struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; 143 + __u32 tmp = 0x10; /* Enable MIPI CSI clock lane */ 144 + 145 + /* Reflect registers immediately */ 146 + iowrite32(0x00000001, priv->base + SH_CSI2_TREF); 147 + /* reset CSI2 harware */ 148 + iowrite32(0x00000001, priv->base + SH_CSI2_SRST); 149 + udelay(5); 150 + iowrite32(0x00000000, priv->base + SH_CSI2_SRST); 151 + 152 + if (priv->client->lanes & 3) 153 + tmp |= priv->client->lanes & 3; 154 + else 155 + /* Default - both lanes */ 156 + tmp |= 3; 157 + 158 + if (priv->client->phy == SH_CSI2_PHY_MAIN) 159 + tmp |= 0x8000; 160 + 161 + iowrite32(tmp, priv->base + SH_CSI2_PHYCNT); 162 + 163 + tmp = 0; 164 + if (pdata->flags & SH_CSI2_ECC) 165 + tmp |= 2; 166 + if (pdata->flags & SH_CSI2_CRC) 167 + tmp |= 1; 168 + iowrite32(tmp, priv->base + SH_CSI2_CHKSUM); 169 + } 170 + 171 + static int sh_csi2_set_bus_param(struct soc_camera_device *icd, 172 + unsigned long flags) 173 + { 174 + return 0; 175 + } 176 + 177 + static unsigned long sh_csi2_query_bus_param(struct soc_camera_device *icd) 178 + { 179 + struct soc_camera_link *icl = to_soc_camera_link(icd); 180 + const unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | 181 + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | 182 + SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_DATA_ACTIVE_HIGH; 183 + 184 + return soc_camera_apply_sensor_flags(icl, flags); 185 + } 186 + 187 + static int sh_csi2_notify(struct notifier_block *nb, 188 + unsigned long action, void *data) 189 + { 190 + struct device *dev = data; 191 + struct soc_camera_device *icd = to_soc_camera_dev(dev); 192 + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev->parent); 193 + struct sh_csi2 *priv = 194 + container_of(nb, struct sh_csi2, notifier); 195 + struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; 196 + int ret, i; 197 + 198 + for (i = 0; i < pdata->num_clients; i++) 199 + if (&pdata->clients[i].pdev->dev == icd->pdev) 200 + break; 201 + 202 + dev_dbg(dev, "%s(%p): action = %lu, found #%d\n", __func__, dev, action, i); 203 + 204 + if (i == pdata->num_clients) 205 + return NOTIFY_DONE; 206 + 207 + switch (action) { 208 + case BUS_NOTIFY_BOUND_DRIVER: 209 + snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s%s", 210 + dev_name(v4l2_dev->dev), ".mipi-csi"); 211 + ret = v4l2_device_register_subdev(v4l2_dev, &priv->subdev); 212 + dev_dbg(dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret); 213 + if (ret < 0) 214 + return NOTIFY_DONE; 215 + 216 + priv->client = pdata->clients + i; 217 + 218 + icd->ops->set_bus_param = sh_csi2_set_bus_param; 219 + icd->ops->query_bus_param = sh_csi2_query_bus_param; 220 + 221 + pm_runtime_get_sync(v4l2_get_subdevdata(&priv->subdev)); 222 + 223 + sh_csi2_hwinit(priv); 224 + break; 225 + case BUS_NOTIFY_UNBIND_DRIVER: 226 + priv->client = NULL; 227 + 228 + /* Driver is about to be unbound */ 229 + icd->ops->set_bus_param = NULL; 230 + icd->ops->query_bus_param = NULL; 231 + 232 + v4l2_device_unregister_subdev(&priv->subdev); 233 + 234 + pm_runtime_put(v4l2_get_subdevdata(&priv->subdev)); 235 + break; 236 + } 237 + 238 + return NOTIFY_OK; 239 + } 240 + 241 + static __devinit int sh_csi2_probe(struct platform_device *pdev) 242 + { 243 + struct resource *res; 244 + unsigned int irq; 245 + int ret; 246 + struct sh_csi2 *priv; 247 + /* Platform data specify the PHY, lanes, ECC, CRC */ 248 + struct sh_csi2_pdata *pdata = pdev->dev.platform_data; 249 + 250 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 251 + /* Interrupt unused so far */ 252 + irq = platform_get_irq(pdev, 0); 253 + 254 + if (!res || (int)irq <= 0 || !pdata) { 255 + dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n"); 256 + return -ENODEV; 257 + } 258 + 259 + /* TODO: Add support for CSI2I. Careful: different register layout! */ 260 + if (pdata->type != SH_CSI2C) { 261 + dev_err(&pdev->dev, "Only CSI2C supported ATM.\n"); 262 + return -EINVAL; 263 + } 264 + 265 + priv = kzalloc(sizeof(struct sh_csi2), GFP_KERNEL); 266 + if (!priv) 267 + return -ENOMEM; 268 + 269 + priv->irq = irq; 270 + priv->notifier.notifier_call = sh_csi2_notify; 271 + 272 + /* We MUST attach after the MIPI sensor */ 273 + ret = bus_register_notifier(&soc_camera_bus_type, &priv->notifier); 274 + if (ret < 0) { 275 + dev_err(&pdev->dev, "CSI2 cannot register notifier\n"); 276 + goto ernotify; 277 + } 278 + 279 + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { 280 + dev_err(&pdev->dev, "CSI2 register region already claimed\n"); 281 + ret = -EBUSY; 282 + goto ereqreg; 283 + } 284 + 285 + priv->base = ioremap(res->start, resource_size(res)); 286 + if (!priv->base) { 287 + ret = -ENXIO; 288 + dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n"); 289 + goto eremap; 290 + } 291 + 292 + priv->pdev = pdev; 293 + 294 + v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops); 295 + v4l2_set_subdevdata(&priv->subdev, &pdev->dev); 296 + 297 + platform_set_drvdata(pdev, priv); 298 + 299 + pm_runtime_enable(&pdev->dev); 300 + 301 + dev_dbg(&pdev->dev, "CSI2 probed.\n"); 302 + 303 + return 0; 304 + 305 + eremap: 306 + release_mem_region(res->start, resource_size(res)); 307 + ereqreg: 308 + bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier); 309 + ernotify: 310 + kfree(priv); 311 + 312 + return ret; 313 + } 314 + 315 + static __devexit int sh_csi2_remove(struct platform_device *pdev) 316 + { 317 + struct sh_csi2 *priv = platform_get_drvdata(pdev); 318 + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 319 + 320 + bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier); 321 + pm_runtime_disable(&pdev->dev); 322 + iounmap(priv->base); 323 + release_mem_region(res->start, resource_size(res)); 324 + platform_set_drvdata(pdev, NULL); 325 + kfree(priv); 326 + 327 + return 0; 328 + } 329 + 330 + static struct platform_driver __refdata sh_csi2_pdrv = { 331 + .remove = __devexit_p(sh_csi2_remove), 332 + .driver = { 333 + .name = "sh-mobile-csi2", 334 + .owner = THIS_MODULE, 335 + }, 336 + }; 337 + 338 + static int __init sh_csi2_init(void) 339 + { 340 + return platform_driver_probe(&sh_csi2_pdrv, sh_csi2_probe); 341 + } 342 + 343 + static void __exit sh_csi2_exit(void) 344 + { 345 + platform_driver_unregister(&sh_csi2_pdrv); 346 + } 347 + 348 + module_init(sh_csi2_init); 349 + module_exit(sh_csi2_exit); 350 + 351 + MODULE_DESCRIPTION("SH-Mobile MIPI CSI-2 driver"); 352 + MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); 353 + MODULE_LICENSE("GPL v2"); 354 + MODULE_ALIAS("platform:sh-mobile-csi2");
+46
include/media/sh_mobile_csi2.h
··· 1 + /* 2 + * Driver header for the SH-Mobile MIPI CSI-2 unit 3 + * 4 + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + 11 + #ifndef SH_MIPI_CSI 12 + #define SH_MIPI_CSI 13 + 14 + enum sh_csi2_phy { 15 + SH_CSI2_PHY_MAIN, 16 + SH_CSI2_PHY_SUB, 17 + }; 18 + 19 + enum sh_csi2_type { 20 + SH_CSI2C, 21 + SH_CSI2I, 22 + }; 23 + 24 + #define SH_CSI2_CRC (1 << 0) 25 + #define SH_CSI2_ECC (1 << 1) 26 + 27 + struct platform_device; 28 + 29 + struct sh_csi2_client_config { 30 + enum sh_csi2_phy phy; 31 + unsigned char lanes; /* bitmask[3:0] */ 32 + unsigned char channel; /* 0..3 */ 33 + struct platform_device *pdev; /* client platform device */ 34 + }; 35 + 36 + struct sh_csi2_pdata { 37 + enum sh_csi2_type type; 38 + unsigned int flags; 39 + struct sh_csi2_client_config *clients; 40 + int num_clients; 41 + }; 42 + 43 + struct device; 44 + struct v4l2_device; 45 + 46 + #endif