aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio
diff options
context:
space:
mode:
authorEduardo Valentin <eduardo.valentin@nokia.com>2009-08-08 07:45:49 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-09-12 11:19:19 -0400
commit1fd2121c08eeef2e9a792719628a467e0fe97b96 (patch)
treea4b71e422eb915d4b0906ba94b15e6beacb2f0d6 /drivers/media/radio
parentfdf82dc2e2d43cf135b5fd352dea523642bb553a (diff)
V4L/DVB (12551): FM TX: si4713: Add files to add radio interface for si4713
This patch adds files which creates the radio interface for si4713 FM transmitter (modulator) devices. In order to do the real access to device registers, this driver uses the v4l2 subdev interface exported by si4713 i2c driver. Signed-off-by: Eduardo Valentin <eduardo.valentin@nokia.com> Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r--drivers/media/radio/radio-si4713.c367
1 files changed, 367 insertions, 0 deletions
diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c
new file mode 100644
index 000000000000..65c14b704586
--- /dev/null
+++ b/drivers/media/radio/radio-si4713.c
@@ -0,0 +1,367 @@
1/*
2 * drivers/media/radio/radio-si4713.c
3 *
4 * Platform Driver for Silicon Labs Si4713 FM Radio Transmitter:
5 *
6 * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT
7 * Contact: Eduardo Valentin <eduardo.valentin@nokia.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/init.h>
27#include <linux/version.h>
28#include <linux/platform_device.h>
29#include <linux/i2c.h>
30#include <linux/videodev2.h>
31#include <media/v4l2-device.h>
32#include <media/v4l2-common.h>
33#include <media/v4l2-ioctl.h>
34#include <media/radio-si4713.h>
35
36/* module parameters */
37static int radio_nr = -1; /* radio device minor (-1 ==> auto assign) */
38module_param(radio_nr, int, 0);
39MODULE_PARM_DESC(radio_nr,
40 "Minor number for radio device (-1 ==> auto assign)");
41
42MODULE_LICENSE("GPL");
43MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>");
44MODULE_DESCRIPTION("Platform driver for Si4713 FM Radio Transmitter");
45MODULE_VERSION("0.0.1");
46
47/* Driver state struct */
48struct radio_si4713_device {
49 struct v4l2_device v4l2_dev;
50 struct video_device *radio_dev;
51};
52
53/* radio_si4713_fops - file operations interface */
54static const struct v4l2_file_operations radio_si4713_fops = {
55 .owner = THIS_MODULE,
56 .ioctl = video_ioctl2,
57};
58
59/* Video4Linux Interface */
60static int radio_si4713_fill_audout(struct v4l2_audioout *vao)
61{
62 /* TODO: check presence of audio output */
63 strlcpy(vao->name, "FM Modulator Audio Out", 32);
64
65 return 0;
66}
67
68static int radio_si4713_enumaudout(struct file *file, void *priv,
69 struct v4l2_audioout *vao)
70{
71 return radio_si4713_fill_audout(vao);
72}
73
74static int radio_si4713_g_audout(struct file *file, void *priv,
75 struct v4l2_audioout *vao)
76{
77 int rval = radio_si4713_fill_audout(vao);
78
79 vao->index = 0;
80
81 return rval;
82}
83
84static int radio_si4713_s_audout(struct file *file, void *priv,
85 struct v4l2_audioout *vao)
86{
87 return vao->index ? -EINVAL : 0;
88}
89
90/* radio_si4713_querycap - query device capabilities */
91static int radio_si4713_querycap(struct file *file, void *priv,
92 struct v4l2_capability *capability)
93{
94 struct radio_si4713_device *rsdev;
95
96 rsdev = video_get_drvdata(video_devdata(file));
97
98 strlcpy(capability->driver, "radio-si4713", sizeof(capability->driver));
99 strlcpy(capability->card, "Silicon Labs Si4713 Modulator",
100 sizeof(capability->card));
101 capability->capabilities = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT;
102
103 return 0;
104}
105
106/* radio_si4713_queryctrl - enumerate control items */
107static int radio_si4713_queryctrl(struct file *file, void *priv,
108 struct v4l2_queryctrl *qc)
109{
110 /* Must be sorted from low to high control ID! */
111 static const u32 user_ctrls[] = {
112 V4L2_CID_USER_CLASS,
113 V4L2_CID_AUDIO_MUTE,
114 0
115 };
116
117 /* Must be sorted from low to high control ID! */
118 static const u32 fmtx_ctrls[] = {
119 V4L2_CID_FM_TX_CLASS,
120 V4L2_CID_RDS_TX_DEVIATION,
121 V4L2_CID_RDS_TX_PI,
122 V4L2_CID_RDS_TX_PTY,
123 V4L2_CID_RDS_TX_PS_NAME,
124 V4L2_CID_RDS_TX_RADIO_TEXT,
125 V4L2_CID_AUDIO_LIMITER_ENABLED,
126 V4L2_CID_AUDIO_LIMITER_RELEASE_TIME,
127 V4L2_CID_AUDIO_LIMITER_DEVIATION,
128 V4L2_CID_AUDIO_COMPRESSION_ENABLED,
129 V4L2_CID_AUDIO_COMPRESSION_GAIN,
130 V4L2_CID_AUDIO_COMPRESSION_THRESHOLD,
131 V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME,
132 V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME,
133 V4L2_CID_PILOT_TONE_ENABLED,
134 V4L2_CID_PILOT_TONE_DEVIATION,
135 V4L2_CID_PILOT_TONE_FREQUENCY,
136 V4L2_CID_TUNE_PREEMPHASIS,
137 V4L2_CID_TUNE_POWER_LEVEL,
138 V4L2_CID_TUNE_ANTENNA_CAPACITOR,
139 0
140 };
141 static const u32 *ctrl_classes[] = {
142 user_ctrls,
143 fmtx_ctrls,
144 NULL
145 };
146 struct radio_si4713_device *rsdev;
147
148 rsdev = video_get_drvdata(video_devdata(file));
149
150 qc->id = v4l2_ctrl_next(ctrl_classes, qc->id);
151 if (qc->id == 0)
152 return -EINVAL;
153
154 if (qc->id == V4L2_CID_USER_CLASS || qc->id == V4L2_CID_FM_TX_CLASS)
155 return v4l2_ctrl_query_fill(qc, 0, 0, 0, 0);
156
157 return v4l2_device_call_until_err(&rsdev->v4l2_dev, 0, core,
158 queryctrl, qc);
159}
160
161/*
162 * v4l2 ioctl call backs.
163 * we are just a wrapper for v4l2_sub_devs.
164 */
165static inline struct v4l2_device *get_v4l2_dev(struct file *file)
166{
167 return &((struct radio_si4713_device *)video_drvdata(file))->v4l2_dev;
168}
169
170static int radio_si4713_g_ext_ctrls(struct file *file, void *p,
171 struct v4l2_ext_controls *vecs)
172{
173 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
174 g_ext_ctrls, vecs);
175}
176
177static int radio_si4713_s_ext_ctrls(struct file *file, void *p,
178 struct v4l2_ext_controls *vecs)
179{
180 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
181 s_ext_ctrls, vecs);
182}
183
184static int radio_si4713_g_ctrl(struct file *file, void *p,
185 struct v4l2_control *vc)
186{
187 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
188 g_ctrl, vc);
189}
190
191static int radio_si4713_s_ctrl(struct file *file, void *p,
192 struct v4l2_control *vc)
193{
194 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
195 s_ctrl, vc);
196}
197
198static int radio_si4713_g_modulator(struct file *file, void *p,
199 struct v4l2_modulator *vm)
200{
201 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
202 g_modulator, vm);
203}
204
205static int radio_si4713_s_modulator(struct file *file, void *p,
206 struct v4l2_modulator *vm)
207{
208 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
209 s_modulator, vm);
210}
211
212static int radio_si4713_g_frequency(struct file *file, void *p,
213 struct v4l2_frequency *vf)
214{
215 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
216 g_frequency, vf);
217}
218
219static int radio_si4713_s_frequency(struct file *file, void *p,
220 struct v4l2_frequency *vf)
221{
222 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
223 s_frequency, vf);
224}
225
226static long radio_si4713_default(struct file *file, void *p, int cmd, void *arg)
227{
228 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
229 ioctl, cmd, arg);
230}
231
232static struct v4l2_ioctl_ops radio_si4713_ioctl_ops = {
233 .vidioc_enumaudout = radio_si4713_enumaudout,
234 .vidioc_g_audout = radio_si4713_g_audout,
235 .vidioc_s_audout = radio_si4713_s_audout,
236 .vidioc_querycap = radio_si4713_querycap,
237 .vidioc_queryctrl = radio_si4713_queryctrl,
238 .vidioc_g_ext_ctrls = radio_si4713_g_ext_ctrls,
239 .vidioc_s_ext_ctrls = radio_si4713_s_ext_ctrls,
240 .vidioc_g_ctrl = radio_si4713_g_ctrl,
241 .vidioc_s_ctrl = radio_si4713_s_ctrl,
242 .vidioc_g_modulator = radio_si4713_g_modulator,
243 .vidioc_s_modulator = radio_si4713_s_modulator,
244 .vidioc_g_frequency = radio_si4713_g_frequency,
245 .vidioc_s_frequency = radio_si4713_s_frequency,
246 .vidioc_default = radio_si4713_default,
247};
248
249/* radio_si4713_vdev_template - video device interface */
250static struct video_device radio_si4713_vdev_template = {
251 .fops = &radio_si4713_fops,
252 .name = "radio-si4713",
253 .release = video_device_release,
254 .ioctl_ops = &radio_si4713_ioctl_ops,
255};
256
257/* Platform driver interface */
258/* radio_si4713_pdriver_probe - probe for the device */
259static int radio_si4713_pdriver_probe(struct platform_device *pdev)
260{
261 struct radio_si4713_platform_data *pdata = pdev->dev.platform_data;
262 struct radio_si4713_device *rsdev;
263 struct i2c_adapter *adapter;
264 struct v4l2_subdev *sd;
265 int rval = 0;
266
267 if (!pdata) {
268 dev_err(&pdev->dev, "Cannot proceed without platform data.\n");
269 rval = -EINVAL;
270 goto exit;
271 }
272
273 rsdev = kzalloc(sizeof *rsdev, GFP_KERNEL);
274 if (!rsdev) {
275 dev_err(&pdev->dev, "Failed to alloc video device.\n");
276 rval = -ENOMEM;
277 goto exit;
278 }
279
280 rval = v4l2_device_register(&pdev->dev, &rsdev->v4l2_dev);
281 if (rval) {
282 dev_err(&pdev->dev, "Failed to register v4l2 device.\n");
283 goto free_rsdev;
284 }
285
286 adapter = i2c_get_adapter(pdata->i2c_bus);
287 if (!adapter) {
288 dev_err(&pdev->dev, "Cannot get i2c adapter %d\n",
289 pdata->i2c_bus);
290 rval = -ENODEV;
291 goto unregister_v4l2_dev;
292 }
293
294 sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter, "si4713_i2c",
295 pdata->subdev_board_info, NULL);
296 if (!sd) {
297 dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n");
298 rval = -ENODEV;
299 goto unregister_v4l2_dev;
300 }
301
302 rsdev->radio_dev = video_device_alloc();
303 if (!rsdev->radio_dev) {
304 dev_err(&pdev->dev, "Failed to alloc video device.\n");
305 rval = -ENOMEM;
306 goto unregister_v4l2_dev;
307 }
308
309 memcpy(rsdev->radio_dev, &radio_si4713_vdev_template,
310 sizeof(radio_si4713_vdev_template));
311 video_set_drvdata(rsdev->radio_dev, rsdev);
312 if (video_register_device(rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) {
313 dev_err(&pdev->dev, "Could not register video device.\n");
314 rval = -EIO;
315 goto free_vdev;
316 }
317 dev_info(&pdev->dev, "New device successfully probed\n");
318
319 goto exit;
320
321free_vdev:
322 video_device_release(rsdev->radio_dev);
323unregister_v4l2_dev:
324 v4l2_device_unregister(&rsdev->v4l2_dev);
325free_rsdev:
326 kfree(rsdev);
327exit:
328 return rval;
329}
330
331/* radio_si4713_pdriver_remove - remove the device */
332static int __exit radio_si4713_pdriver_remove(struct platform_device *pdev)
333{
334 struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
335 struct radio_si4713_device *rsdev = container_of(v4l2_dev,
336 struct radio_si4713_device,
337 v4l2_dev);
338
339 video_unregister_device(rsdev->radio_dev);
340 v4l2_device_unregister(&rsdev->v4l2_dev);
341 kfree(rsdev);
342
343 return 0;
344}
345
346static struct platform_driver radio_si4713_pdriver = {
347 .driver = {
348 .name = "radio-si4713",
349 },
350 .probe = radio_si4713_pdriver_probe,
351 .remove = __exit_p(radio_si4713_pdriver_remove),
352};
353
354/* Module Interface */
355static int __init radio_si4713_module_init(void)
356{
357 return platform_driver_register(&radio_si4713_pdriver);
358}
359
360static void __exit radio_si4713_module_exit(void)
361{
362 platform_driver_unregister(&radio_si4713_pdriver);
363}
364
365module_init(radio_si4713_module_init);
366module_exit(radio_si4713_module_exit);
367