Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * radio-timb.c Timberdale FPGA Radio driver
0004  * Copyright (c) 2009 Intel Corporation
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);