Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002  /*
0003     tda9840 - i2c-driver for the tda9840 by SGS Thomson
0004 
0005     Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
0006     Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
0007 
0008     The tda9840 is a stereo/dual sound processor with digital
0009     identification. It can be found at address 0x84 on the i2c-bus.
0010 
0011     For detailed information download the specifications directly
0012     from SGS Thomson at http://www.st.com
0013 
0014   */
0015 
0016 
0017 #include <linux/module.h>
0018 #include <linux/ioctl.h>
0019 #include <linux/slab.h>
0020 #include <linux/i2c.h>
0021 #include <media/v4l2-device.h>
0022 
0023 MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
0024 MODULE_DESCRIPTION("tda9840 driver");
0025 MODULE_LICENSE("GPL");
0026 
0027 static int debug;
0028 module_param(debug, int, 0644);
0029 
0030 MODULE_PARM_DESC(debug, "Debug level (0-1)");
0031 
0032 #define SWITCH      0x00
0033 #define LEVEL_ADJUST    0x02
0034 #define STEREO_ADJUST   0x03
0035 #define TEST        0x04
0036 
0037 #define TDA9840_SET_MUTE                0x00
0038 #define TDA9840_SET_MONO                0x10
0039 #define TDA9840_SET_STEREO              0x2a
0040 #define TDA9840_SET_LANG1               0x12
0041 #define TDA9840_SET_LANG2               0x1e
0042 #define TDA9840_SET_BOTH                0x1a
0043 #define TDA9840_SET_BOTH_R              0x16
0044 #define TDA9840_SET_EXTERNAL            0x7a
0045 
0046 
0047 static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val)
0048 {
0049     struct i2c_client *client = v4l2_get_subdevdata(sd);
0050 
0051     if (i2c_smbus_write_byte_data(client, reg, val))
0052         v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n",
0053                 val, reg);
0054 }
0055 
0056 static int tda9840_status(struct v4l2_subdev *sd)
0057 {
0058     struct i2c_client *client = v4l2_get_subdevdata(sd);
0059     int rc;
0060     u8 byte;
0061 
0062     rc = i2c_master_recv(client, &byte, 1);
0063     if (rc != 1) {
0064         v4l2_dbg(1, debug, sd,
0065             "i2c_master_recv() failed\n");
0066         if (rc < 0)
0067             return rc;
0068         return -EIO;
0069     }
0070 
0071     if (byte & 0x80) {
0072         v4l2_dbg(1, debug, sd,
0073             "TDA9840_DETECT: register contents invalid\n");
0074         return -EINVAL;
0075     }
0076 
0077     v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte);
0078     return byte & 0x60;
0079 }
0080 
0081 static int tda9840_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *t)
0082 {
0083     int stat = tda9840_status(sd);
0084     int byte;
0085 
0086     if (t->index)
0087         return -EINVAL;
0088 
0089     stat = stat < 0 ? 0 : stat;
0090     if (stat == 0 || stat == 0x60) /* mono input */
0091         byte = TDA9840_SET_MONO;
0092     else if (stat == 0x40) /* stereo input */
0093         byte = (t->audmode == V4L2_TUNER_MODE_MONO) ?
0094             TDA9840_SET_MONO : TDA9840_SET_STEREO;
0095     else { /* bilingual */
0096         switch (t->audmode) {
0097         case V4L2_TUNER_MODE_LANG1_LANG2:
0098             byte = TDA9840_SET_BOTH;
0099             break;
0100         case V4L2_TUNER_MODE_LANG2:
0101             byte = TDA9840_SET_LANG2;
0102             break;
0103         default:
0104             byte = TDA9840_SET_LANG1;
0105             break;
0106         }
0107     }
0108     v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte);
0109     tda9840_write(sd, SWITCH, byte);
0110     return 0;
0111 }
0112 
0113 static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
0114 {
0115     int stat = tda9840_status(sd);
0116 
0117     if (stat < 0)
0118         return stat;
0119 
0120     t->rxsubchans = V4L2_TUNER_SUB_MONO;
0121 
0122     switch (stat & 0x60) {
0123     case 0x00:
0124         t->rxsubchans = V4L2_TUNER_SUB_MONO;
0125         break;
0126     case 0x20:
0127         t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
0128         break;
0129     case 0x40:
0130         t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
0131         break;
0132     default: /* Incorrect detect */
0133         t->rxsubchans = V4L2_TUNER_MODE_MONO;
0134         break;
0135     }
0136     return 0;
0137 }
0138 
0139 /* ----------------------------------------------------------------------- */
0140 
0141 static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = {
0142     .s_tuner = tda9840_s_tuner,
0143     .g_tuner = tda9840_g_tuner,
0144 };
0145 
0146 static const struct v4l2_subdev_ops tda9840_ops = {
0147     .tuner = &tda9840_tuner_ops,
0148 };
0149 
0150 /* ----------------------------------------------------------------------- */
0151 
0152 static int tda9840_probe(struct i2c_client *client,
0153               const struct i2c_device_id *id)
0154 {
0155     struct v4l2_subdev *sd;
0156 
0157     /* let's see whether this adapter can support what we need */
0158     if (!i2c_check_functionality(client->adapter,
0159             I2C_FUNC_SMBUS_READ_BYTE_DATA |
0160             I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
0161         return -EIO;
0162 
0163     v4l_info(client, "chip found @ 0x%x (%s)\n",
0164             client->addr << 1, client->adapter->name);
0165 
0166     sd = devm_kzalloc(&client->dev, sizeof(*sd), GFP_KERNEL);
0167     if (sd == NULL)
0168         return -ENOMEM;
0169     v4l2_i2c_subdev_init(sd, client, &tda9840_ops);
0170 
0171     /* set initial values for level & stereo - adjustment, mode */
0172     tda9840_write(sd, LEVEL_ADJUST, 0);
0173     tda9840_write(sd, STEREO_ADJUST, 0);
0174     tda9840_write(sd, SWITCH, TDA9840_SET_STEREO);
0175     return 0;
0176 }
0177 
0178 static int tda9840_remove(struct i2c_client *client)
0179 {
0180     struct v4l2_subdev *sd = i2c_get_clientdata(client);
0181 
0182     v4l2_device_unregister_subdev(sd);
0183     return 0;
0184 }
0185 
0186 static const struct i2c_device_id tda9840_id[] = {
0187     { "tda9840", 0 },
0188     { }
0189 };
0190 MODULE_DEVICE_TABLE(i2c, tda9840_id);
0191 
0192 static struct i2c_driver tda9840_driver = {
0193     .driver = {
0194         .name   = "tda9840",
0195     },
0196     .probe      = tda9840_probe,
0197     .remove     = tda9840_remove,
0198     .id_table   = tda9840_id,
0199 };
0200 
0201 module_i2c_driver(tda9840_driver);