linux/drivers/media/radio/radio-si4713.c
<<
>>
Prefs
   1/*
   2 * drivers/media/radio/radio-si4713.c
   3 *
   4 * Platform Driver for Silicon Labs Si4713 FM Radio Transmitter:
   5 *
   6 * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT
   7 * Contact: Eduardo Valentin <eduardo.valentin@nokia.com>
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2 of the License, or
  12 * (at your option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software
  21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  22 */
  23
  24#include <linux/kernel.h>
  25#include <linux/module.h>
  26#include <linux/init.h>
  27#include <linux/platform_device.h>
  28#include <linux/i2c.h>
  29#include <linux/videodev2.h>
  30#include <media/v4l2-device.h>
  31#include <media/v4l2-common.h>
  32#include <media/v4l2-ioctl.h>
  33#include <media/radio-si4713.h>
  34
  35/* module parameters */
  36static int radio_nr = -1;       /* radio device minor (-1 ==> auto assign) */
  37module_param(radio_nr, int, 0);
  38MODULE_PARM_DESC(radio_nr,
  39                 "Minor number for radio device (-1 ==> auto assign)");
  40
  41MODULE_LICENSE("GPL");
  42MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>");
  43MODULE_DESCRIPTION("Platform driver for Si4713 FM Radio Transmitter");
  44MODULE_VERSION("0.0.1");
  45
  46/* Driver state struct */
  47struct radio_si4713_device {
  48        struct v4l2_device              v4l2_dev;
  49        struct video_device             *radio_dev;
  50};
  51
  52/* radio_si4713_fops - file operations interface */
  53static const struct v4l2_file_operations radio_si4713_fops = {
  54        .owner          = THIS_MODULE,
  55        .ioctl          = video_ioctl2,
  56};
  57
  58/* Video4Linux Interface */
  59static int radio_si4713_fill_audout(struct v4l2_audioout *vao)
  60{
  61        /* TODO: check presence of audio output */
  62        strlcpy(vao->name, "FM Modulator Audio Out", 32);
  63
  64        return 0;
  65}
  66
  67static int radio_si4713_enumaudout(struct file *file, void *priv,
  68                                                struct v4l2_audioout *vao)
  69{
  70        return radio_si4713_fill_audout(vao);
  71}
  72
  73static int radio_si4713_g_audout(struct file *file, void *priv,
  74                                        struct v4l2_audioout *vao)
  75{
  76        int rval = radio_si4713_fill_audout(vao);
  77
  78        vao->index = 0;
  79
  80        return rval;
  81}
  82
  83static int radio_si4713_s_audout(struct file *file, void *priv,
  84                                        struct v4l2_audioout *vao)
  85{
  86        return vao->index ? -EINVAL : 0;
  87}
  88
  89/* radio_si4713_querycap - query device capabilities */
  90static int radio_si4713_querycap(struct file *file, void *priv,
  91                                        struct v4l2_capability *capability)
  92{
  93        struct radio_si4713_device *rsdev;
  94
  95        rsdev = video_get_drvdata(video_devdata(file));
  96
  97        strlcpy(capability->driver, "radio-si4713", sizeof(capability->driver));
  98        strlcpy(capability->card, "Silicon Labs Si4713 Modulator",
  99                                sizeof(capability->card));
 100        capability->capabilities = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT;
 101
 102        return 0;
 103}
 104
 105/* radio_si4713_queryctrl - enumerate control items */
 106static int radio_si4713_queryctrl(struct file *file, void *priv,
 107                                                struct v4l2_queryctrl *qc)
 108{
 109        /* Must be sorted from low to high control ID! */
 110        static const u32 user_ctrls[] = {
 111                V4L2_CID_USER_CLASS,
 112                V4L2_CID_AUDIO_MUTE,
 113                0
 114        };
 115
 116        /* Must be sorted from low to high control ID! */
 117        static const u32 fmtx_ctrls[] = {
 118                V4L2_CID_FM_TX_CLASS,
 119                V4L2_CID_RDS_TX_DEVIATION,
 120                V4L2_CID_RDS_TX_PI,
 121                V4L2_CID_RDS_TX_PTY,
 122                V4L2_CID_RDS_TX_PS_NAME,
 123                V4L2_CID_RDS_TX_RADIO_TEXT,
 124                V4L2_CID_AUDIO_LIMITER_ENABLED,
 125                V4L2_CID_AUDIO_LIMITER_RELEASE_TIME,
 126                V4L2_CID_AUDIO_LIMITER_DEVIATION,
 127                V4L2_CID_AUDIO_COMPRESSION_ENABLED,
 128                V4L2_CID_AUDIO_COMPRESSION_GAIN,
 129                V4L2_CID_AUDIO_COMPRESSION_THRESHOLD,
 130                V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME,
 131                V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME,
 132                V4L2_CID_PILOT_TONE_ENABLED,
 133                V4L2_CID_PILOT_TONE_DEVIATION,
 134                V4L2_CID_PILOT_TONE_FREQUENCY,
 135                V4L2_CID_TUNE_PREEMPHASIS,
 136                V4L2_CID_TUNE_POWER_LEVEL,
 137                V4L2_CID_TUNE_ANTENNA_CAPACITOR,
 138                0
 139        };
 140        static const u32 *ctrl_classes[] = {
 141                user_ctrls,
 142                fmtx_ctrls,
 143                NULL
 144        };
 145        struct radio_si4713_device *rsdev;
 146
 147        rsdev = video_get_drvdata(video_devdata(file));
 148
 149        qc->id = v4l2_ctrl_next(ctrl_classes, qc->id);
 150        if (qc->id == 0)
 151                return -EINVAL;
 152
 153        if (qc->id == V4L2_CID_USER_CLASS || qc->id == V4L2_CID_FM_TX_CLASS)
 154                return v4l2_ctrl_query_fill(qc, 0, 0, 0, 0);
 155
 156        return v4l2_device_call_until_err(&rsdev->v4l2_dev, 0, core,
 157                                                queryctrl, qc);
 158}
 159
 160/*
 161 * v4l2 ioctl call backs.
 162 * we are just a wrapper for v4l2_sub_devs.
 163 */
 164static inline struct v4l2_device *get_v4l2_dev(struct file *file)
 165{
 166        return &((struct radio_si4713_device *)video_drvdata(file))->v4l2_dev;
 167}
 168
 169static int radio_si4713_g_ext_ctrls(struct file *file, void *p,
 170                                                struct v4l2_ext_controls *vecs)
 171{
 172        return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
 173                                                        g_ext_ctrls, vecs);
 174}
 175
 176static int radio_si4713_s_ext_ctrls(struct file *file, void *p,
 177                                                struct v4l2_ext_controls *vecs)
 178{
 179        return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
 180                                                        s_ext_ctrls, vecs);
 181}
 182
 183static int radio_si4713_g_ctrl(struct file *file, void *p,
 184                                                struct v4l2_control *vc)
 185{
 186        return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
 187                                                        g_ctrl, vc);
 188}
 189
 190static int radio_si4713_s_ctrl(struct file *file, void *p,
 191                                                struct v4l2_control *vc)
 192{
 193        return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
 194                                                        s_ctrl, vc);
 195}
 196
 197static int radio_si4713_g_modulator(struct file *file, void *p,
 198                                                struct v4l2_modulator *vm)
 199{
 200        return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
 201                                                        g_modulator, vm);
 202}
 203
 204static int radio_si4713_s_modulator(struct file *file, void *p,
 205                                                struct v4l2_modulator *vm)
 206{
 207        return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
 208                                                        s_modulator, vm);
 209}
 210
 211static int radio_si4713_g_frequency(struct file *file, void *p,
 212                                                struct v4l2_frequency *vf)
 213{
 214        return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
 215                                                        g_frequency, vf);
 216}
 217
 218static int radio_si4713_s_frequency(struct file *file, void *p,
 219                                                struct v4l2_frequency *vf)
 220{
 221        return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
 222                                                        s_frequency, vf);
 223}
 224
 225static long radio_si4713_default(struct file *file, void *p, int cmd, void *arg)
 226{
 227        return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
 228                                                        ioctl, cmd, arg);
 229}
 230
 231static struct v4l2_ioctl_ops radio_si4713_ioctl_ops = {
 232        .vidioc_enumaudout      = radio_si4713_enumaudout,
 233        .vidioc_g_audout        = radio_si4713_g_audout,
 234        .vidioc_s_audout        = radio_si4713_s_audout,
 235        .vidioc_querycap        = radio_si4713_querycap,
 236        .vidioc_queryctrl       = radio_si4713_queryctrl,
 237        .vidioc_g_ext_ctrls     = radio_si4713_g_ext_ctrls,
 238        .vidioc_s_ext_ctrls     = radio_si4713_s_ext_ctrls,
 239        .vidioc_g_ctrl          = radio_si4713_g_ctrl,
 240        .vidioc_s_ctrl          = radio_si4713_s_ctrl,
 241        .vidioc_g_modulator     = radio_si4713_g_modulator,
 242        .vidioc_s_modulator     = radio_si4713_s_modulator,
 243        .vidioc_g_frequency     = radio_si4713_g_frequency,
 244        .vidioc_s_frequency     = radio_si4713_s_frequency,
 245        .vidioc_default         = radio_si4713_default,
 246};
 247
 248/* radio_si4713_vdev_template - video device interface */
 249static struct video_device radio_si4713_vdev_template = {
 250        .fops                   = &radio_si4713_fops,
 251        .name                   = "radio-si4713",
 252        .release                = video_device_release,
 253        .ioctl_ops              = &radio_si4713_ioctl_ops,
 254};
 255
 256/* Platform driver interface */
 257/* radio_si4713_pdriver_probe - probe for the device */
 258static int radio_si4713_pdriver_probe(struct platform_device *pdev)
 259{
 260        struct radio_si4713_platform_data *pdata = pdev->dev.platform_data;
 261        struct radio_si4713_device *rsdev;
 262        struct i2c_adapter *adapter;
 263        struct v4l2_subdev *sd;
 264        int rval = 0;
 265
 266        if (!pdata) {
 267                dev_err(&pdev->dev, "Cannot proceed without platform data.\n");
 268                rval = -EINVAL;
 269                goto exit;
 270        }
 271
 272        rsdev = kzalloc(sizeof *rsdev, GFP_KERNEL);
 273        if (!rsdev) {
 274                dev_err(&pdev->dev, "Failed to alloc video device.\n");
 275                rval = -ENOMEM;
 276                goto exit;
 277        }
 278
 279        rval = v4l2_device_register(&pdev->dev, &rsdev->v4l2_dev);
 280        if (rval) {
 281                dev_err(&pdev->dev, "Failed to register v4l2 device.\n");
 282                goto free_rsdev;
 283        }
 284
 285        adapter = i2c_get_adapter(pdata->i2c_bus);
 286        if (!adapter) {
 287                dev_err(&pdev->dev, "Cannot get i2c adapter %d\n",
 288                                                        pdata->i2c_bus);
 289                rval = -ENODEV;
 290                goto unregister_v4l2_dev;
 291        }
 292
 293        sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter, "si4713_i2c",
 294                                        pdata->subdev_board_info, NULL);
 295        if (!sd) {
 296                dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n");
 297                rval = -ENODEV;
 298                goto unregister_v4l2_dev;
 299        }
 300
 301        rsdev->radio_dev = video_device_alloc();
 302        if (!rsdev->radio_dev) {
 303                dev_err(&pdev->dev, "Failed to alloc video device.\n");
 304                rval = -ENOMEM;
 305                goto unregister_v4l2_dev;
 306        }
 307
 308        memcpy(rsdev->radio_dev, &radio_si4713_vdev_template,
 309                        sizeof(radio_si4713_vdev_template));
 310        video_set_drvdata(rsdev->radio_dev, rsdev);
 311        if (video_register_device(rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) {
 312                dev_err(&pdev->dev, "Could not register video device.\n");
 313                rval = -EIO;
 314                goto free_vdev;
 315        }
 316        dev_info(&pdev->dev, "New device successfully probed\n");
 317
 318        goto exit;
 319
 320free_vdev:
 321        video_device_release(rsdev->radio_dev);
 322unregister_v4l2_dev:
 323        v4l2_device_unregister(&rsdev->v4l2_dev);
 324free_rsdev:
 325        kfree(rsdev);
 326exit:
 327        return rval;
 328}
 329
 330/* radio_si4713_pdriver_remove - remove the device */
 331static int __exit radio_si4713_pdriver_remove(struct platform_device *pdev)
 332{
 333        struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
 334        struct radio_si4713_device *rsdev = container_of(v4l2_dev,
 335                                                struct radio_si4713_device,
 336                                                v4l2_dev);
 337
 338        video_unregister_device(rsdev->radio_dev);
 339        v4l2_device_unregister(&rsdev->v4l2_dev);
 340        kfree(rsdev);
 341
 342        return 0;
 343}
 344
 345static struct platform_driver radio_si4713_pdriver = {
 346        .driver         = {
 347                .name   = "radio-si4713",
 348        },
 349        .probe          = radio_si4713_pdriver_probe,
 350        .remove         = __exit_p(radio_si4713_pdriver_remove),
 351};
 352
 353/* Module Interface */
 354static int __init radio_si4713_module_init(void)
 355{
 356        return platform_driver_register(&radio_si4713_pdriver);
 357}
 358
 359static void __exit radio_si4713_module_exit(void)
 360{
 361        platform_driver_unregister(&radio_si4713_pdriver);
 362}
 363
 364module_init(radio_si4713_module_init);
 365module_exit(radio_si4713_module_exit);
 366
 367
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.