• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/media/video/
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
33struct 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
43static 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 */
91static 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
128static 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
133static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops;
134
135static struct v4l2_subdev_ops sh_csi2_subdev_ops = {
136	.core	= &sh_csi2_subdev_core_ops,
137	.video	= &sh_csi2_subdev_video_ops,
138};
139
140static 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
171static int sh_csi2_set_bus_param(struct soc_camera_device *icd,
172				 unsigned long flags)
173{
174	return 0;
175}
176
177static 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
187static 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
241static __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
305eremap:
306	release_mem_region(res->start, resource_size(res));
307ereqreg:
308	bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier);
309ernotify:
310	kfree(priv);
311
312	return ret;
313}
314
315static __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
330static 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
338static int __init sh_csi2_init(void)
339{
340	return platform_driver_probe(&sh_csi2_pdrv, sh_csi2_probe);
341}
342
343static void __exit sh_csi2_exit(void)
344{
345	platform_driver_unregister(&sh_csi2_pdrv);
346}
347
348module_init(sh_csi2_init);
349module_exit(sh_csi2_exit);
350
351MODULE_DESCRIPTION("SH-Mobile MIPI CSI-2 driver");
352MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
353MODULE_LICENSE("GPL v2");
354MODULE_ALIAS("platform:sh-mobile-csi2");
355