Commit | Line | Data |
---|---|---|
326c9862 MD |
1 | /* |
2 | * Generic Platform Camera Driver | |
3 | * | |
4 | * Copyright (C) 2008 Magnus Damm | |
5 | * Based on mt9m001 driver, | |
6 | * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
13 | #include <linux/init.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/platform_device.h> | |
18 | #include <linux/videodev2.h> | |
19 | #include <media/v4l2-common.h> | |
20 | #include <media/soc_camera.h> | |
50c616fd | 21 | #include <media/soc_camera_platform.h> |
326c9862 MD |
22 | |
23 | struct soc_camera_platform_priv { | |
326c9862 MD |
24 | struct soc_camera_data_format format; |
25 | }; | |
26 | ||
27 | static struct soc_camera_platform_info * | |
28 | soc_camera_platform_get_info(struct soc_camera_device *icd) | |
29 | { | |
40e2e092 GL |
30 | struct platform_device *pdev = to_platform_device(dev_get_drvdata(&icd->dev)); |
31 | return pdev->dev.platform_data; | |
326c9862 MD |
32 | } |
33 | ||
34 | static int soc_camera_platform_init(struct soc_camera_device *icd) | |
35 | { | |
40e2e092 | 36 | struct soc_camera_link *icl = to_soc_camera_link(icd); |
50c616fd | 37 | |
40e2e092 GL |
38 | if (icl->power) |
39 | icl->power(dev_get_drvdata(&icd->dev), 1); | |
50c616fd | 40 | |
326c9862 MD |
41 | return 0; |
42 | } | |
43 | ||
44 | static int soc_camera_platform_release(struct soc_camera_device *icd) | |
45 | { | |
40e2e092 | 46 | struct soc_camera_link *icl = to_soc_camera_link(icd); |
50c616fd | 47 | |
40e2e092 GL |
48 | if (icl->power) |
49 | icl->power(dev_get_drvdata(&icd->dev), 0); | |
50c616fd | 50 | |
326c9862 MD |
51 | return 0; |
52 | } | |
53 | ||
54 | static int soc_camera_platform_start_capture(struct soc_camera_device *icd) | |
55 | { | |
56 | struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); | |
57 | return p->set_capture(p, 1); | |
58 | } | |
59 | ||
60 | static int soc_camera_platform_stop_capture(struct soc_camera_device *icd) | |
61 | { | |
62 | struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); | |
63 | return p->set_capture(p, 0); | |
64 | } | |
65 | ||
66 | static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd, | |
67 | unsigned long flags) | |
68 | { | |
69 | return 0; | |
70 | } | |
71 | ||
72 | static unsigned long | |
73 | soc_camera_platform_query_bus_param(struct soc_camera_device *icd) | |
74 | { | |
75 | struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); | |
76 | return p->bus_param; | |
77 | } | |
78 | ||
09e231b3 GL |
79 | static int soc_camera_platform_set_crop(struct soc_camera_device *icd, |
80 | struct v4l2_rect *rect) | |
81 | { | |
82 | return 0; | |
83 | } | |
84 | ||
d8fac217 | 85 | static int soc_camera_platform_set_fmt(struct soc_camera_device *icd, |
09e231b3 | 86 | struct v4l2_format *f) |
326c9862 MD |
87 | { |
88 | return 0; | |
89 | } | |
90 | ||
d8fac217 GL |
91 | static int soc_camera_platform_try_fmt(struct soc_camera_device *icd, |
92 | struct v4l2_format *f) | |
326c9862 MD |
93 | { |
94 | struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); | |
64f5905e | 95 | struct v4l2_pix_format *pix = &f->fmt.pix; |
326c9862 | 96 | |
64f5905e GL |
97 | pix->width = p->format.width; |
98 | pix->height = p->format.height; | |
326c9862 MD |
99 | return 0; |
100 | } | |
101 | ||
40e2e092 GL |
102 | static int soc_camera_platform_video_probe(struct soc_camera_device *icd, |
103 | struct platform_device *pdev) | |
326c9862 | 104 | { |
40e2e092 GL |
105 | struct soc_camera_platform_priv *priv = platform_get_drvdata(pdev); |
106 | struct soc_camera_platform_info *p = pdev->dev.platform_data; | |
107 | int ret; | |
326c9862 | 108 | |
40e2e092 GL |
109 | priv->format.name = p->format_name; |
110 | priv->format.depth = p->format_depth; | |
111 | priv->format.fourcc = p->format.pixelformat; | |
112 | priv->format.colorspace = p->format.colorspace; | |
326c9862 MD |
113 | |
114 | icd->formats = &priv->format; | |
115 | icd->num_formats = 1; | |
116 | ||
40e2e092 GL |
117 | /* ..._video_start() does dev_set_drvdata(&icd->dev, &pdev->dev) */ |
118 | ret = soc_camera_video_start(icd, &pdev->dev); | |
326c9862 | 119 | soc_camera_video_stop(icd); |
40e2e092 | 120 | return ret; |
326c9862 MD |
121 | } |
122 | ||
123 | static struct soc_camera_ops soc_camera_platform_ops = { | |
124 | .owner = THIS_MODULE, | |
326c9862 MD |
125 | .init = soc_camera_platform_init, |
126 | .release = soc_camera_platform_release, | |
127 | .start_capture = soc_camera_platform_start_capture, | |
128 | .stop_capture = soc_camera_platform_stop_capture, | |
09e231b3 | 129 | .set_crop = soc_camera_platform_set_crop, |
d8fac217 GL |
130 | .set_fmt = soc_camera_platform_set_fmt, |
131 | .try_fmt = soc_camera_platform_try_fmt, | |
326c9862 MD |
132 | .set_bus_param = soc_camera_platform_set_bus_param, |
133 | .query_bus_param = soc_camera_platform_query_bus_param, | |
134 | }; | |
135 | ||
136 | static int soc_camera_platform_probe(struct platform_device *pdev) | |
137 | { | |
138 | struct soc_camera_platform_priv *priv; | |
40e2e092 | 139 | struct soc_camera_platform_info *p = pdev->dev.platform_data; |
326c9862 MD |
140 | struct soc_camera_device *icd; |
141 | int ret; | |
142 | ||
326c9862 MD |
143 | if (!p) |
144 | return -EINVAL; | |
145 | ||
146 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | |
147 | if (!priv) | |
148 | return -ENOMEM; | |
149 | ||
326c9862 MD |
150 | platform_set_drvdata(pdev, priv); |
151 | ||
40e2e092 GL |
152 | icd = to_soc_camera_dev(p->dev); |
153 | if (!icd) | |
154 | goto enoicd; | |
155 | ||
326c9862 | 156 | icd->ops = &soc_camera_platform_ops; |
40e2e092 | 157 | dev_set_drvdata(&icd->dev, &pdev->dev); |
326c9862 | 158 | icd->width_min = 0; |
40e2e092 | 159 | icd->width_max = p->format.width; |
326c9862 | 160 | icd->height_min = 0; |
40e2e092 | 161 | icd->height_max = p->format.height; |
326c9862 | 162 | icd->y_skip_top = 0; |
326c9862 | 163 | |
40e2e092 GL |
164 | ret = soc_camera_platform_video_probe(icd, pdev); |
165 | if (ret) { | |
166 | icd->ops = NULL; | |
326c9862 | 167 | kfree(priv); |
40e2e092 | 168 | } |
326c9862 MD |
169 | |
170 | return ret; | |
40e2e092 GL |
171 | |
172 | enoicd: | |
173 | kfree(priv); | |
174 | return -EINVAL; | |
326c9862 MD |
175 | } |
176 | ||
177 | static int soc_camera_platform_remove(struct platform_device *pdev) | |
178 | { | |
179 | struct soc_camera_platform_priv *priv = platform_get_drvdata(pdev); | |
40e2e092 GL |
180 | struct soc_camera_platform_info *p = pdev->dev.platform_data; |
181 | struct soc_camera_device *icd = to_soc_camera_dev(p->dev); | |
326c9862 | 182 | |
40e2e092 | 183 | icd->ops = NULL; |
326c9862 MD |
184 | kfree(priv); |
185 | return 0; | |
186 | } | |
187 | ||
188 | static struct platform_driver soc_camera_platform_driver = { | |
189 | .driver = { | |
190 | .name = "soc_camera_platform", | |
191 | }, | |
192 | .probe = soc_camera_platform_probe, | |
193 | .remove = soc_camera_platform_remove, | |
194 | }; | |
195 | ||
196 | static int __init soc_camera_platform_module_init(void) | |
197 | { | |
198 | return platform_driver_register(&soc_camera_platform_driver); | |
199 | } | |
200 | ||
201 | static void __exit soc_camera_platform_module_exit(void) | |
202 | { | |
01c1e4ca | 203 | platform_driver_unregister(&soc_camera_platform_driver); |
326c9862 MD |
204 | } |
205 | ||
206 | module_init(soc_camera_platform_module_init); | |
207 | module_exit(soc_camera_platform_module_exit); | |
208 | ||
209 | MODULE_DESCRIPTION("SoC Camera Platform driver"); | |
210 | MODULE_AUTHOR("Magnus Damm"); | |
211 | MODULE_LICENSE("GPL v2"); | |
40e2e092 | 212 | MODULE_ALIAS("platform:soc_camera_platform"); |