0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/init.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/i2c.h>
0016 #include <linux/videodev2.h>
0017 #include <linux/slab.h>
0018 #include <media/v4l2-device.h>
0019 #include <media/v4l2-common.h>
0020 #include <media/v4l2-ioctl.h>
0021 #include <media/v4l2-fh.h>
0022 #include <media/v4l2-ctrls.h>
0023 #include <media/v4l2-event.h>
0024 #include "si4713.h"
0025
0026
0027 static int radio_nr = -1;
0028 module_param(radio_nr, int, 0);
0029 MODULE_PARM_DESC(radio_nr,
0030 "Minor number for radio device (-1 ==> auto assign)");
0031
0032 MODULE_LICENSE("GPL v2");
0033 MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>");
0034 MODULE_DESCRIPTION("Platform driver for Si4713 FM Radio Transmitter");
0035 MODULE_VERSION("0.0.1");
0036 MODULE_ALIAS("platform:radio-si4713");
0037
0038
0039 struct radio_si4713_device {
0040 struct v4l2_device v4l2_dev;
0041 struct video_device radio_dev;
0042 struct mutex lock;
0043 };
0044
0045
0046 static const struct v4l2_file_operations radio_si4713_fops = {
0047 .owner = THIS_MODULE,
0048 .open = v4l2_fh_open,
0049 .release = v4l2_fh_release,
0050 .poll = v4l2_ctrl_poll,
0051
0052 .unlocked_ioctl = video_ioctl2,
0053 };
0054
0055
0056
0057
0058 static int radio_si4713_querycap(struct file *file, void *priv,
0059 struct v4l2_capability *capability)
0060 {
0061 strscpy(capability->driver, "radio-si4713", sizeof(capability->driver));
0062 strscpy(capability->card, "Silicon Labs Si4713 Modulator",
0063 sizeof(capability->card));
0064 strscpy(capability->bus_info, "platform:radio-si4713",
0065 sizeof(capability->bus_info));
0066 return 0;
0067 }
0068
0069
0070
0071
0072
0073 static inline struct v4l2_device *get_v4l2_dev(struct file *file)
0074 {
0075 return &((struct radio_si4713_device *)video_drvdata(file))->v4l2_dev;
0076 }
0077
0078 static int radio_si4713_g_modulator(struct file *file, void *p,
0079 struct v4l2_modulator *vm)
0080 {
0081 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
0082 g_modulator, vm);
0083 }
0084
0085 static int radio_si4713_s_modulator(struct file *file, void *p,
0086 const struct v4l2_modulator *vm)
0087 {
0088 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
0089 s_modulator, vm);
0090 }
0091
0092 static int radio_si4713_g_frequency(struct file *file, void *p,
0093 struct v4l2_frequency *vf)
0094 {
0095 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
0096 g_frequency, vf);
0097 }
0098
0099 static int radio_si4713_s_frequency(struct file *file, void *p,
0100 const struct v4l2_frequency *vf)
0101 {
0102 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
0103 s_frequency, vf);
0104 }
0105
0106 static long radio_si4713_default(struct file *file, void *p,
0107 bool valid_prio, unsigned int cmd, void *arg)
0108 {
0109 return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
0110 ioctl, cmd, arg);
0111 }
0112
0113 static const struct v4l2_ioctl_ops radio_si4713_ioctl_ops = {
0114 .vidioc_querycap = radio_si4713_querycap,
0115 .vidioc_g_modulator = radio_si4713_g_modulator,
0116 .vidioc_s_modulator = radio_si4713_s_modulator,
0117 .vidioc_g_frequency = radio_si4713_g_frequency,
0118 .vidioc_s_frequency = radio_si4713_s_frequency,
0119 .vidioc_log_status = v4l2_ctrl_log_status,
0120 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
0121 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
0122 .vidioc_default = radio_si4713_default,
0123 };
0124
0125
0126 static const struct video_device radio_si4713_vdev_template = {
0127 .fops = &radio_si4713_fops,
0128 .name = "radio-si4713",
0129 .release = video_device_release_empty,
0130 .ioctl_ops = &radio_si4713_ioctl_ops,
0131 .vfl_dir = VFL_DIR_TX,
0132 };
0133
0134
0135
0136 static int radio_si4713_pdriver_probe(struct platform_device *pdev)
0137 {
0138 struct radio_si4713_platform_data *pdata = pdev->dev.platform_data;
0139 struct radio_si4713_device *rsdev;
0140 struct v4l2_subdev *sd;
0141 int rval = 0;
0142
0143 if (!pdata) {
0144 dev_err(&pdev->dev, "Cannot proceed without platform data.\n");
0145 rval = -EINVAL;
0146 goto exit;
0147 }
0148
0149 rsdev = devm_kzalloc(&pdev->dev, sizeof(*rsdev), GFP_KERNEL);
0150 if (!rsdev) {
0151 dev_err(&pdev->dev, "Failed to alloc video device.\n");
0152 rval = -ENOMEM;
0153 goto exit;
0154 }
0155 mutex_init(&rsdev->lock);
0156
0157 rval = v4l2_device_register(&pdev->dev, &rsdev->v4l2_dev);
0158 if (rval) {
0159 dev_err(&pdev->dev, "Failed to register v4l2 device.\n");
0160 goto exit;
0161 }
0162
0163 sd = i2c_get_clientdata(pdata->subdev);
0164 rval = v4l2_device_register_subdev(&rsdev->v4l2_dev, sd);
0165 if (rval) {
0166 dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n");
0167 goto unregister_v4l2_dev;
0168 }
0169
0170 rsdev->radio_dev = radio_si4713_vdev_template;
0171 rsdev->radio_dev.v4l2_dev = &rsdev->v4l2_dev;
0172 rsdev->radio_dev.ctrl_handler = sd->ctrl_handler;
0173
0174 rsdev->radio_dev.lock = &rsdev->lock;
0175 rsdev->radio_dev.device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT;
0176 video_set_drvdata(&rsdev->radio_dev, rsdev);
0177 if (video_register_device(&rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) {
0178 dev_err(&pdev->dev, "Could not register video device.\n");
0179 rval = -EIO;
0180 goto unregister_v4l2_dev;
0181 }
0182 dev_info(&pdev->dev, "New device successfully probed\n");
0183
0184 goto exit;
0185
0186 unregister_v4l2_dev:
0187 v4l2_device_unregister(&rsdev->v4l2_dev);
0188 exit:
0189 return rval;
0190 }
0191
0192
0193 static int radio_si4713_pdriver_remove(struct platform_device *pdev)
0194 {
0195 struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
0196 struct radio_si4713_device *rsdev;
0197
0198 rsdev = container_of(v4l2_dev, struct radio_si4713_device, v4l2_dev);
0199 video_unregister_device(&rsdev->radio_dev);
0200 v4l2_device_unregister(&rsdev->v4l2_dev);
0201
0202 return 0;
0203 }
0204
0205 static struct platform_driver radio_si4713_pdriver = {
0206 .driver = {
0207 .name = "radio-si4713",
0208 },
0209 .probe = radio_si4713_pdriver_probe,
0210 .remove = radio_si4713_pdriver_remove,
0211 };
0212
0213 module_platform_driver(radio_si4713_pdriver);