0001
0002
0003
0004
0005
0006
0007 #include <linux/io.h>
0008 #include <media/v4l2-ioctl.h>
0009 #include <media/v4l2-device.h>
0010 #include <media/v4l2-ctrls.h>
0011 #include <media/v4l2-event.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/slab.h>
0015 #include <linux/i2c.h>
0016 #include <linux/module.h>
0017 #include <linux/platform_data/media/timb_radio.h>
0018
0019 #define DRIVER_NAME "timb-radio"
0020
0021 struct timbradio {
0022 struct timb_radio_platform_data pdata;
0023 struct v4l2_subdev *sd_tuner;
0024 struct v4l2_subdev *sd_dsp;
0025 struct video_device video_dev;
0026 struct v4l2_device v4l2_dev;
0027 struct mutex lock;
0028 };
0029
0030
0031 static int timbradio_vidioc_querycap(struct file *file, void *priv,
0032 struct v4l2_capability *v)
0033 {
0034 strscpy(v->driver, DRIVER_NAME, sizeof(v->driver));
0035 strscpy(v->card, "Timberdale Radio", sizeof(v->card));
0036 snprintf(v->bus_info, sizeof(v->bus_info), "platform:"DRIVER_NAME);
0037 return 0;
0038 }
0039
0040 static int timbradio_vidioc_g_tuner(struct file *file, void *priv,
0041 struct v4l2_tuner *v)
0042 {
0043 struct timbradio *tr = video_drvdata(file);
0044 return v4l2_subdev_call(tr->sd_tuner, tuner, g_tuner, v);
0045 }
0046
0047 static int timbradio_vidioc_s_tuner(struct file *file, void *priv,
0048 const struct v4l2_tuner *v)
0049 {
0050 struct timbradio *tr = video_drvdata(file);
0051 return v4l2_subdev_call(tr->sd_tuner, tuner, s_tuner, v);
0052 }
0053
0054 static int timbradio_vidioc_s_frequency(struct file *file, void *priv,
0055 const struct v4l2_frequency *f)
0056 {
0057 struct timbradio *tr = video_drvdata(file);
0058 return v4l2_subdev_call(tr->sd_tuner, tuner, s_frequency, f);
0059 }
0060
0061 static int timbradio_vidioc_g_frequency(struct file *file, void *priv,
0062 struct v4l2_frequency *f)
0063 {
0064 struct timbradio *tr = video_drvdata(file);
0065 return v4l2_subdev_call(tr->sd_tuner, tuner, g_frequency, f);
0066 }
0067
0068 static const struct v4l2_ioctl_ops timbradio_ioctl_ops = {
0069 .vidioc_querycap = timbradio_vidioc_querycap,
0070 .vidioc_g_tuner = timbradio_vidioc_g_tuner,
0071 .vidioc_s_tuner = timbradio_vidioc_s_tuner,
0072 .vidioc_g_frequency = timbradio_vidioc_g_frequency,
0073 .vidioc_s_frequency = timbradio_vidioc_s_frequency,
0074 .vidioc_log_status = v4l2_ctrl_log_status,
0075 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
0076 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
0077 };
0078
0079 static const struct v4l2_file_operations timbradio_fops = {
0080 .owner = THIS_MODULE,
0081 .open = v4l2_fh_open,
0082 .release = v4l2_fh_release,
0083 .poll = v4l2_ctrl_poll,
0084 .unlocked_ioctl = video_ioctl2,
0085 };
0086
0087 static int timbradio_probe(struct platform_device *pdev)
0088 {
0089 struct timb_radio_platform_data *pdata = pdev->dev.platform_data;
0090 struct timbradio *tr;
0091 int err;
0092
0093 if (!pdata) {
0094 dev_err(&pdev->dev, "Platform data missing\n");
0095 err = -EINVAL;
0096 goto err;
0097 }
0098
0099 tr = devm_kzalloc(&pdev->dev, sizeof(*tr), GFP_KERNEL);
0100 if (!tr) {
0101 err = -ENOMEM;
0102 goto err;
0103 }
0104
0105 tr->pdata = *pdata;
0106 mutex_init(&tr->lock);
0107
0108 strscpy(tr->video_dev.name, "Timberdale Radio",
0109 sizeof(tr->video_dev.name));
0110 tr->video_dev.fops = &timbradio_fops;
0111 tr->video_dev.ioctl_ops = &timbradio_ioctl_ops;
0112 tr->video_dev.release = video_device_release_empty;
0113 tr->video_dev.minor = -1;
0114 tr->video_dev.lock = &tr->lock;
0115 tr->video_dev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
0116
0117 strscpy(tr->v4l2_dev.name, DRIVER_NAME, sizeof(tr->v4l2_dev.name));
0118 err = v4l2_device_register(NULL, &tr->v4l2_dev);
0119 if (err)
0120 goto err;
0121
0122 tr->video_dev.v4l2_dev = &tr->v4l2_dev;
0123
0124 tr->sd_tuner = v4l2_i2c_new_subdev_board(&tr->v4l2_dev,
0125 i2c_get_adapter(pdata->i2c_adapter), pdata->tuner, NULL);
0126 tr->sd_dsp = v4l2_i2c_new_subdev_board(&tr->v4l2_dev,
0127 i2c_get_adapter(pdata->i2c_adapter), pdata->dsp, NULL);
0128 if (tr->sd_tuner == NULL || tr->sd_dsp == NULL) {
0129 err = -ENODEV;
0130 goto err_video_req;
0131 }
0132
0133 tr->v4l2_dev.ctrl_handler = tr->sd_dsp->ctrl_handler;
0134
0135 err = video_register_device(&tr->video_dev, VFL_TYPE_RADIO, -1);
0136 if (err) {
0137 dev_err(&pdev->dev, "Error reg video\n");
0138 goto err_video_req;
0139 }
0140
0141 video_set_drvdata(&tr->video_dev, tr);
0142
0143 platform_set_drvdata(pdev, tr);
0144 return 0;
0145
0146 err_video_req:
0147 v4l2_device_unregister(&tr->v4l2_dev);
0148 err:
0149 dev_err(&pdev->dev, "Failed to register: %d\n", err);
0150
0151 return err;
0152 }
0153
0154 static int timbradio_remove(struct platform_device *pdev)
0155 {
0156 struct timbradio *tr = platform_get_drvdata(pdev);
0157
0158 video_unregister_device(&tr->video_dev);
0159 v4l2_device_unregister(&tr->v4l2_dev);
0160 return 0;
0161 }
0162
0163 static struct platform_driver timbradio_platform_driver = {
0164 .driver = {
0165 .name = DRIVER_NAME,
0166 },
0167 .probe = timbradio_probe,
0168 .remove = timbradio_remove,
0169 };
0170
0171 module_platform_driver(timbradio_platform_driver);
0172
0173 MODULE_DESCRIPTION("Timberdale Radio driver");
0174 MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
0175 MODULE_LICENSE("GPL v2");
0176 MODULE_VERSION("0.0.2");
0177 MODULE_ALIAS("platform:"DRIVER_NAME);