brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · 9fdaed6 Raw
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