212 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * drivers/media/radio/radio-si4713.c4 *5 * Platform Driver for Silicon Labs Si4713 FM Radio Transmitter:6 *7 * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT8 * Contact: Eduardo Valentin <eduardo.valentin@nokia.com>9 */10 11#include <linux/kernel.h>12#include <linux/module.h>13#include <linux/init.h>14#include <linux/platform_device.h>15#include <linux/i2c.h>16#include <linux/videodev2.h>17#include <linux/slab.h>18#include <media/v4l2-device.h>19#include <media/v4l2-common.h>20#include <media/v4l2-ioctl.h>21#include <media/v4l2-fh.h>22#include <media/v4l2-ctrls.h>23#include <media/v4l2-event.h>24#include "si4713.h"25 26/* module parameters */27static int radio_nr = -1; /* radio device minor (-1 ==> auto assign) */28module_param(radio_nr, int, 0);29MODULE_PARM_DESC(radio_nr,30 "Minor number for radio device (-1 ==> auto assign)");31 32MODULE_LICENSE("GPL v2");33MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>");34MODULE_DESCRIPTION("Platform driver for Si4713 FM Radio Transmitter");35MODULE_VERSION("0.0.1");36MODULE_ALIAS("platform:radio-si4713");37 38/* Driver state struct */39struct radio_si4713_device {40 struct v4l2_device v4l2_dev;41 struct video_device radio_dev;42 struct mutex lock;43};44 45/* radio_si4713_fops - file operations interface */46static const struct v4l2_file_operations radio_si4713_fops = {47 .owner = THIS_MODULE,48 .open = v4l2_fh_open,49 .release = v4l2_fh_release,50 .poll = v4l2_ctrl_poll,51 /* Note: locking is done at the subdev level in the i2c driver. */52 .unlocked_ioctl = video_ioctl2,53};54 55/* Video4Linux Interface */56 57/* radio_si4713_querycap - query device capabilities */58static int radio_si4713_querycap(struct file *file, void *priv,59 struct v4l2_capability *capability)60{61 strscpy(capability->driver, "radio-si4713", sizeof(capability->driver));62 strscpy(capability->card, "Silicon Labs Si4713 Modulator",63 sizeof(capability->card));64 strscpy(capability->bus_info, "platform:radio-si4713",65 sizeof(capability->bus_info));66 return 0;67}68 69/*70 * v4l2 ioctl call backs.71 * we are just a wrapper for v4l2_sub_devs.72 */73static inline struct v4l2_device *get_v4l2_dev(struct file *file)74{75 return &((struct radio_si4713_device *)video_drvdata(file))->v4l2_dev;76}77 78static int radio_si4713_g_modulator(struct file *file, void *p,79 struct v4l2_modulator *vm)80{81 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,82 g_modulator, vm);83}84 85static int radio_si4713_s_modulator(struct file *file, void *p,86 const struct v4l2_modulator *vm)87{88 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,89 s_modulator, vm);90}91 92static int radio_si4713_g_frequency(struct file *file, void *p,93 struct v4l2_frequency *vf)94{95 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,96 g_frequency, vf);97}98 99static int radio_si4713_s_frequency(struct file *file, void *p,100 const struct v4l2_frequency *vf)101{102 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,103 s_frequency, vf);104}105 106static long radio_si4713_default(struct file *file, void *p,107 bool valid_prio, unsigned int cmd, void *arg)108{109 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,110 ioctl, cmd, arg);111}112 113static const struct v4l2_ioctl_ops radio_si4713_ioctl_ops = {114 .vidioc_querycap = radio_si4713_querycap,115 .vidioc_g_modulator = radio_si4713_g_modulator,116 .vidioc_s_modulator = radio_si4713_s_modulator,117 .vidioc_g_frequency = radio_si4713_g_frequency,118 .vidioc_s_frequency = radio_si4713_s_frequency,119 .vidioc_log_status = v4l2_ctrl_log_status,120 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,121 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,122 .vidioc_default = radio_si4713_default,123};124 125/* radio_si4713_vdev_template - video device interface */126static const struct video_device radio_si4713_vdev_template = {127 .fops = &radio_si4713_fops,128 .name = "radio-si4713",129 .release = video_device_release_empty,130 .ioctl_ops = &radio_si4713_ioctl_ops,131 .vfl_dir = VFL_DIR_TX,132};133 134/* Platform driver interface */135/* radio_si4713_pdriver_probe - probe for the device */136static int radio_si4713_pdriver_probe(struct platform_device *pdev)137{138 struct radio_si4713_platform_data *pdata = pdev->dev.platform_data;139 struct radio_si4713_device *rsdev;140 struct v4l2_subdev *sd;141 int rval = 0;142 143 if (!pdata) {144 dev_err(&pdev->dev, "Cannot proceed without platform data.\n");145 rval = -EINVAL;146 goto exit;147 }148 149 rsdev = devm_kzalloc(&pdev->dev, sizeof(*rsdev), GFP_KERNEL);150 if (!rsdev) {151 dev_err(&pdev->dev, "Failed to alloc video device.\n");152 rval = -ENOMEM;153 goto exit;154 }155 mutex_init(&rsdev->lock);156 157 rval = v4l2_device_register(&pdev->dev, &rsdev->v4l2_dev);158 if (rval) {159 dev_err(&pdev->dev, "Failed to register v4l2 device.\n");160 goto exit;161 }162 163 sd = i2c_get_clientdata(pdata->subdev);164 rval = v4l2_device_register_subdev(&rsdev->v4l2_dev, sd);165 if (rval) {166 dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n");167 goto unregister_v4l2_dev;168 }169 170 rsdev->radio_dev = radio_si4713_vdev_template;171 rsdev->radio_dev.v4l2_dev = &rsdev->v4l2_dev;172 rsdev->radio_dev.ctrl_handler = sd->ctrl_handler;173 /* Serialize all access to the si4713 */174 rsdev->radio_dev.lock = &rsdev->lock;175 rsdev->radio_dev.device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT;176 video_set_drvdata(&rsdev->radio_dev, rsdev);177 if (video_register_device(&rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) {178 dev_err(&pdev->dev, "Could not register video device.\n");179 rval = -EIO;180 goto unregister_v4l2_dev;181 }182 dev_info(&pdev->dev, "New device successfully probed\n");183 184 goto exit;185 186unregister_v4l2_dev:187 v4l2_device_unregister(&rsdev->v4l2_dev);188exit:189 return rval;190}191 192/* radio_si4713_pdriver_remove - remove the device */193static void radio_si4713_pdriver_remove(struct platform_device *pdev)194{195 struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);196 struct radio_si4713_device *rsdev;197 198 rsdev = container_of(v4l2_dev, struct radio_si4713_device, v4l2_dev);199 video_unregister_device(&rsdev->radio_dev);200 v4l2_device_unregister(&rsdev->v4l2_dev);201}202 203static struct platform_driver radio_si4713_pdriver = {204 .driver = {205 .name = "radio-si4713",206 },207 .probe = radio_si4713_pdriver_probe,208 .remove_new = radio_si4713_pdriver_remove,209};210 211module_platform_driver(radio_si4713_pdriver);212