1
2
3
4
5
6
7
8
9
10
11#include <linux/delay.h>
12#include <linux/err.h>
13#include <linux/i2c.h>
14#include <linux/io.h>
15#include <linux/platform_device.h>
16#include <linux/pm_runtime.h>
17#include <linux/slab.h>
18#include <linux/videodev2.h>
19#include <linux/module.h>
20
21#include <media/sh_mobile_ceu.h>
22#include <media/sh_mobile_csi2.h>
23#include <media/soc_camera.h>
24#include <media/soc_mediabus.h>
25#include <media/v4l2-common.h>
26#include <media/v4l2-dev.h>
27#include <media/v4l2-device.h>
28#include <media/v4l2-mediabus.h>
29#include <media/v4l2-subdev.h>
30
31#define SH_CSI2_TREF 0x00
32#define SH_CSI2_SRST 0x04
33#define SH_CSI2_PHYCNT 0x08
34#define SH_CSI2_CHKSUM 0x0C
35#define SH_CSI2_VCDT 0x10
36
37struct sh_csi2 {
38 struct v4l2_subdev subdev;
39 struct list_head list;
40 unsigned int irq;
41 unsigned long mipi_flags;
42 void __iomem *base;
43 struct platform_device *pdev;
44 struct sh_csi2_client_config *client;
45};
46
47static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
48 struct v4l2_mbus_framefmt *mf)
49{
50 struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
51 struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
52
53 if (mf->width > 8188)
54 mf->width = 8188;
55 else if (mf->width & 1)
56 mf->width &= ~1;
57
58 switch (pdata->type) {
59 case SH_CSI2C:
60 switch (mf->code) {
61 case V4L2_MBUS_FMT_UYVY8_2X8:
62 case V4L2_MBUS_FMT_YUYV8_1_5X8:
63 case V4L2_MBUS_FMT_Y8_1X8:
64 case V4L2_MBUS_FMT_SBGGR8_1X8:
65 case V4L2_MBUS_FMT_SGRBG8_1X8:
66 break;
67 default:
68
69 mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
70 }
71 break;
72 case SH_CSI2I:
73 switch (mf->code) {
74 case V4L2_MBUS_FMT_Y8_1X8:
75 case V4L2_MBUS_FMT_SBGGR8_1X8:
76 case V4L2_MBUS_FMT_SGRBG8_1X8:
77 case V4L2_MBUS_FMT_SBGGR10_1X10:
78 case V4L2_MBUS_FMT_SBGGR12_1X12:
79 break;
80 default:
81
82 mf->code = V4L2_MBUS_FMT_SBGGR8_1X8;
83 }
84 break;
85 }
86
87 return 0;
88}
89
90
91
92
93
94
95static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
96 struct v4l2_mbus_framefmt *mf)
97{
98 struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
99 u32 tmp = (priv->client->channel & 3) << 8;
100
101 dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
102 if (mf->width > 8188 || mf->width & 1)
103 return -EINVAL;
104
105 switch (mf->code) {
106 case V4L2_MBUS_FMT_UYVY8_2X8:
107 tmp |= 0x1e;
108 break;
109 case V4L2_MBUS_FMT_YUYV8_1_5X8:
110 tmp |= 0x18;
111 break;
112 case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
113 tmp |= 0x21;
114 break;
115 case V4L2_MBUS_FMT_RGB565_2X8_BE:
116 tmp |= 0x22;
117 break;
118 case V4L2_MBUS_FMT_Y8_1X8:
119 case V4L2_MBUS_FMT_SBGGR8_1X8:
120 case V4L2_MBUS_FMT_SGRBG8_1X8:
121 tmp |= 0x2a;
122 break;
123 default:
124 return -EINVAL;
125 }
126
127 iowrite32(tmp, priv->base + SH_CSI2_VCDT);
128
129 return 0;
130}
131
132static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
133 struct v4l2_mbus_config *cfg)
134{
135 cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
136 V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
137 V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
138 cfg->type = V4L2_MBUS_PARALLEL;
139
140 return 0;
141}
142
143static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd,
144 const struct v4l2_mbus_config *cfg)
145{
146 struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
147 struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
148 struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
149 struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,
150 .flags = priv->mipi_flags};
151
152 return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
153}
154
155static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = {
156 .s_mbus_fmt = sh_csi2_s_fmt,
157 .try_mbus_fmt = sh_csi2_try_fmt,
158 .g_mbus_config = sh_csi2_g_mbus_config,
159 .s_mbus_config = sh_csi2_s_mbus_config,
160};
161
162static void sh_csi2_hwinit(struct sh_csi2 *priv)
163{
164 struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
165 __u32 tmp = 0x10;
166
167
168 iowrite32(0x00000001, priv->base + SH_CSI2_TREF);
169
170 iowrite32(0x00000001, priv->base + SH_CSI2_SRST);
171 udelay(5);
172 iowrite32(0x00000000, priv->base + SH_CSI2_SRST);
173
174 switch (pdata->type) {
175 case SH_CSI2C:
176 if (priv->client->lanes == 1)
177 tmp |= 1;
178 else
179
180 tmp |= 3;
181 break;
182 case SH_CSI2I:
183 if (!priv->client->lanes || priv->client->lanes > 4)
184
185 tmp |= 0xf;
186 else
187 tmp |= (1 << priv->client->lanes) - 1;
188 }
189
190 if (priv->client->phy == SH_CSI2_PHY_MAIN)
191 tmp |= 0x8000;
192
193 iowrite32(tmp, priv->base + SH_CSI2_PHYCNT);
194
195 tmp = 0;
196 if (pdata->flags & SH_CSI2_ECC)
197 tmp |= 2;
198 if (pdata->flags & SH_CSI2_CRC)
199 tmp |= 1;
200 iowrite32(tmp, priv->base + SH_CSI2_CHKSUM);
201}
202
203static int sh_csi2_client_connect(struct sh_csi2 *priv)
204{
205 struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
206 struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
207 struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
208 struct device *dev = v4l2_get_subdevdata(&priv->subdev);
209 struct v4l2_mbus_config cfg;
210 unsigned long common_flags, csi2_flags;
211 int i, ret;
212
213 if (priv->client)
214 return -EBUSY;
215
216 for (i = 0; i < pdata->num_clients; i++)
217 if (&pdata->clients[i].pdev->dev == icd->pdev)
218 break;
219
220 dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i);
221
222 if (i == pdata->num_clients)
223 return -ENODEV;
224
225
226 csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE;
227
228 switch (pdata->type) {
229 case SH_CSI2C:
230 if (pdata->clients[i].lanes != 1)
231 csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
232 break;
233 case SH_CSI2I:
234 switch (pdata->clients[i].lanes) {
235 default:
236 csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
237 case 3:
238 csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
239 case 2:
240 csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
241 }
242 }
243
244 cfg.type = V4L2_MBUS_CSI2;
245 ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg);
246 if (ret == -ENOIOCTLCMD)
247 common_flags = csi2_flags;
248 else if (!ret)
249 common_flags = soc_mbus_config_compatible(&cfg,
250 csi2_flags);
251 else
252 common_flags = 0;
253
254 if (!common_flags)
255 return -EINVAL;
256
257
258 priv->mipi_flags = common_flags;
259 priv->client = pdata->clients + i;
260
261 pm_runtime_get_sync(dev);
262
263 sh_csi2_hwinit(priv);
264
265 return 0;
266}
267
268static void sh_csi2_client_disconnect(struct sh_csi2 *priv)
269{
270 if (!priv->client)
271 return;
272
273 priv->client = NULL;
274
275 pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
276}
277
278static int sh_csi2_s_power(struct v4l2_subdev *sd, int on)
279{
280 struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
281
282 if (on)
283 return sh_csi2_client_connect(priv);
284
285 sh_csi2_client_disconnect(priv);
286 return 0;
287}
288
289static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops = {
290 .s_power = sh_csi2_s_power,
291};
292
293static struct v4l2_subdev_ops sh_csi2_subdev_ops = {
294 .core = &sh_csi2_subdev_core_ops,
295 .video = &sh_csi2_subdev_video_ops,
296};
297
298static int sh_csi2_probe(struct platform_device *pdev)
299{
300 struct resource *res;
301 unsigned int irq;
302 int ret;
303 struct sh_csi2 *priv;
304
305 struct sh_csi2_pdata *pdata = pdev->dev.platform_data;
306
307 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
308
309 irq = platform_get_irq(pdev, 0);
310
311 if (!res || (int)irq <= 0 || !pdata) {
312 dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n");
313 return -ENODEV;
314 }
315
316
317 if (pdata->type != SH_CSI2C) {
318 dev_err(&pdev->dev, "Only CSI2C supported ATM.\n");
319 return -EINVAL;
320 }
321
322 priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL);
323 if (!priv)
324 return -ENOMEM;
325
326 priv->irq = irq;
327
328 priv->base = devm_ioremap_resource(&pdev->dev, res);
329 if (IS_ERR(priv->base))
330 return PTR_ERR(priv->base);
331
332 priv->pdev = pdev;
333 platform_set_drvdata(pdev, priv);
334
335 v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops);
336 v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
337
338 snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi",
339 dev_name(pdata->v4l2_dev->dev));
340 ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev);
341 dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
342 if (ret < 0)
343 goto esdreg;
344
345 pm_runtime_enable(&pdev->dev);
346
347 dev_dbg(&pdev->dev, "CSI2 probed.\n");
348
349 return 0;
350
351esdreg:
352 platform_set_drvdata(pdev, NULL);
353
354 return ret;
355}
356
357static int sh_csi2_remove(struct platform_device *pdev)
358{
359 struct sh_csi2 *priv = platform_get_drvdata(pdev);
360
361 v4l2_device_unregister_subdev(&priv->subdev);
362 pm_runtime_disable(&pdev->dev);
363 platform_set_drvdata(pdev, NULL);
364
365 return 0;
366}
367
368static struct platform_driver __refdata sh_csi2_pdrv = {
369 .remove = sh_csi2_remove,
370 .probe = sh_csi2_probe,
371 .driver = {
372 .name = "sh-mobile-csi2",
373 .owner = THIS_MODULE,
374 },
375};
376
377module_platform_driver(sh_csi2_pdrv);
378
379MODULE_DESCRIPTION("SH-Mobile MIPI CSI-2 driver");
380MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
381MODULE_LICENSE("GPL v2");
382MODULE_ALIAS("platform:sh-mobile-csi2");
383