383 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2005-2006 Micronas USA Inc.4 */5 6#include <linux/module.h>7#include <linux/init.h>8#include <linux/i2c.h>9#include <linux/videodev2.h>10#include <media/tuner.h>11#include <media/v4l2-common.h>12#include <media/v4l2-ioctl.h>13#include <media/v4l2-device.h>14#include <linux/slab.h>15 16MODULE_DESCRIPTION("sony-btf-mpx driver");17MODULE_LICENSE("GPL v2");18 19static int debug;20module_param(debug, int, 0644);21MODULE_PARM_DESC(debug, "debug level 0=off(default) 1=on");22 23/* #define MPX_DEBUG */24 25/*26 * Note:27 *28 * AS(IF/MPX) pin: LOW HIGH/OPEN29 * IF/MPX address: 0x42/0x40 0x43/0x4430 */31 32 33static int force_mpx_mode = -1;34module_param(force_mpx_mode, int, 0644);35 36struct sony_btf_mpx {37 struct v4l2_subdev sd;38 int mpxmode;39 u32 audmode;40};41 42static inline struct sony_btf_mpx *to_state(struct v4l2_subdev *sd)43{44 return container_of(sd, struct sony_btf_mpx, sd);45}46 47static int mpx_write(struct i2c_client *client, int dev, int addr, int val)48{49 u8 buffer[5];50 struct i2c_msg msg;51 52 buffer[0] = dev;53 buffer[1] = addr >> 8;54 buffer[2] = addr & 0xff;55 buffer[3] = val >> 8;56 buffer[4] = val & 0xff;57 msg.addr = client->addr;58 msg.flags = 0;59 msg.len = 5;60 msg.buf = buffer;61 i2c_transfer(client->adapter, &msg, 1);62 return 0;63}64 65/*66 * MPX register values for the BTF-PG472Z:67 *68 * FM_ NICAM_ SCART_69 * MODUS SOURCE ACB PRESCAL PRESCAL PRESCAL SYSTEM VOLUME70 * 10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/000071 * ---------------------------------------------------------------72 * Auto 1003 0020 0100 2603 5000 XXXX 0001 750073 *74 * B/G75 * Mono 1003 0020 0100 2603 5000 XXXX 0003 750076 * A2 1003 0020 0100 2601 5000 XXXX 0003 750077 * NICAM 1003 0120 0100 2603 5000 XXXX 0008 750078 *79 * I80 * Mono 1003 0020 0100 2603 7900 XXXX 000A 750081 * NICAM 1003 0120 0100 2603 7900 XXXX 000A 750082 *83 * D/K84 * Mono 1003 0020 0100 2603 5000 XXXX 0004 750085 * A2-1 1003 0020 0100 2601 5000 XXXX 0004 750086 * A2-2 1003 0020 0100 2601 5000 XXXX 0005 750087 * A2-3 1003 0020 0100 2601 5000 XXXX 0007 750088 * NICAM 1003 0120 0100 2603 5000 XXXX 000B 750089 *90 * L/L'91 * Mono 0003 0200 0100 7C03 5000 2200 0009 750092 * NICAM 0003 0120 0100 7C03 5000 XXXX 0009 750093 *94 * M95 * Mono 1003 0200 0100 2B03 5000 2B00 0002 750096 *97 * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX.98 *99 * Bilingual selection in A2/NICAM:100 *101 * High byte of SOURCE Left chan Right chan102 * 0x01 MAIN SUB103 * 0x03 MAIN MAIN104 * 0x04 SUB SUB105 *106 * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or107 * 0x00 (all other bands). Force mono in A2 with FMONO_A2:108 *109 * FMONO_A2110 * 10/0022111 * --------112 * Forced mono ON 07F0113 * Forced mono OFF 0190114 */115 116static const struct {117 enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode;118 u16 modus;119 u16 source;120 u16 acb;121 u16 fm_prescale;122 u16 nicam_prescale;123 u16 scart_prescale;124 u16 system;125 u16 volume;126} mpx_audio_modes[] = {127 /* Auto */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,128 0x5000, 0x0000, 0x0001, 0x7500 },129 /* B/G Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,130 0x5000, 0x0000, 0x0003, 0x7500 },131 /* B/G A2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,132 0x5000, 0x0000, 0x0003, 0x7500 },133 /* B/G NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,134 0x5000, 0x0000, 0x0008, 0x7500 },135 /* I Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,136 0x7900, 0x0000, 0x000A, 0x7500 },137 /* I NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,138 0x7900, 0x0000, 0x000A, 0x7500 },139 /* D/K Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,140 0x5000, 0x0000, 0x0004, 0x7500 },141 /* D/K A2-1 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,142 0x5000, 0x0000, 0x0004, 0x7500 },143 /* D/K A2-2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,144 0x5000, 0x0000, 0x0005, 0x7500 },145 /* D/K A2-3 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,146 0x5000, 0x0000, 0x0007, 0x7500 },147 /* D/K NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,148 0x5000, 0x0000, 0x000B, 0x7500 },149 /* L/L' Mono */ { AUD_MONO, 0x0003, 0x0200, 0x0100, 0x7C03,150 0x5000, 0x2200, 0x0009, 0x7500 },151 /* L/L' NICAM */{ AUD_NICAM_L, 0x0003, 0x0120, 0x0100, 0x7C03,152 0x5000, 0x0000, 0x0009, 0x7500 },153};154 155#define MPX_NUM_MODES ARRAY_SIZE(mpx_audio_modes)156 157static int mpx_setup(struct sony_btf_mpx *t)158{159 struct i2c_client *client = v4l2_get_subdevdata(&t->sd);160 u16 source = 0;161 u8 buffer[3];162 struct i2c_msg msg;163 int mode = t->mpxmode;164 165 /* reset MPX */166 buffer[0] = 0x00;167 buffer[1] = 0x80;168 buffer[2] = 0x00;169 msg.addr = client->addr;170 msg.flags = 0;171 msg.len = 3;172 msg.buf = buffer;173 i2c_transfer(client->adapter, &msg, 1);174 buffer[1] = 0x00;175 i2c_transfer(client->adapter, &msg, 1);176 177 if (t->audmode != V4L2_TUNER_MODE_MONO)178 mode++;179 180 if (mpx_audio_modes[mode].audio_mode != AUD_MONO) {181 switch (t->audmode) {182 case V4L2_TUNER_MODE_MONO:183 switch (mpx_audio_modes[mode].audio_mode) {184 case AUD_A2:185 source = mpx_audio_modes[mode].source;186 break;187 case AUD_NICAM:188 source = 0x0000;189 break;190 case AUD_NICAM_L:191 source = 0x0200;192 break;193 default:194 break;195 }196 break;197 case V4L2_TUNER_MODE_STEREO:198 source = mpx_audio_modes[mode].source;199 break;200 case V4L2_TUNER_MODE_LANG1:201 source = 0x0300;202 break;203 case V4L2_TUNER_MODE_LANG2:204 source = 0x0400;205 break;206 }207 source |= mpx_audio_modes[mode].source & 0x00ff;208 } else209 source = mpx_audio_modes[mode].source;210 211 mpx_write(client, 0x10, 0x0030, mpx_audio_modes[mode].modus);212 mpx_write(client, 0x12, 0x0008, source);213 mpx_write(client, 0x12, 0x0013, mpx_audio_modes[mode].acb);214 mpx_write(client, 0x12, 0x000e,215 mpx_audio_modes[mode].fm_prescale);216 mpx_write(client, 0x12, 0x0010,217 mpx_audio_modes[mode].nicam_prescale);218 mpx_write(client, 0x12, 0x000d,219 mpx_audio_modes[mode].scart_prescale);220 mpx_write(client, 0x10, 0x0020, mpx_audio_modes[mode].system);221 mpx_write(client, 0x12, 0x0000, mpx_audio_modes[mode].volume);222 if (mpx_audio_modes[mode].audio_mode == AUD_A2)223 mpx_write(client, 0x10, 0x0022,224 t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190);225 226#ifdef MPX_DEBUG227 {228 u8 buf1[3], buf2[2];229 struct i2c_msg msgs[2];230 231 v4l2_info(client,232 "MPX registers: %04x %04x %04x %04x %04x %04x %04x %04x\n",233 mpx_audio_modes[mode].modus,234 source,235 mpx_audio_modes[mode].acb,236 mpx_audio_modes[mode].fm_prescale,237 mpx_audio_modes[mode].nicam_prescale,238 mpx_audio_modes[mode].scart_prescale,239 mpx_audio_modes[mode].system,240 mpx_audio_modes[mode].volume);241 buf1[0] = 0x11;242 buf1[1] = 0x00;243 buf1[2] = 0x7e;244 msgs[0].addr = client->addr;245 msgs[0].flags = 0;246 msgs[0].len = 3;247 msgs[0].buf = buf1;248 msgs[1].addr = client->addr;249 msgs[1].flags = I2C_M_RD;250 msgs[1].len = 2;251 msgs[1].buf = buf2;252 i2c_transfer(client->adapter, msgs, 2);253 v4l2_info(client, "MPX system: %02x%02x\n",254 buf2[0], buf2[1]);255 buf1[0] = 0x11;256 buf1[1] = 0x02;257 buf1[2] = 0x00;258 i2c_transfer(client->adapter, msgs, 2);259 v4l2_info(client, "MPX status: %02x%02x\n",260 buf2[0], buf2[1]);261 }262#endif263 return 0;264}265 266 267static int sony_btf_mpx_s_std(struct v4l2_subdev *sd, v4l2_std_id std)268{269 struct sony_btf_mpx *t = to_state(sd);270 int default_mpx_mode = 0;271 272 if (std & V4L2_STD_PAL_BG)273 default_mpx_mode = 1;274 else if (std & V4L2_STD_PAL_I)275 default_mpx_mode = 4;276 else if (std & V4L2_STD_PAL_DK)277 default_mpx_mode = 6;278 else if (std & V4L2_STD_SECAM_L)279 default_mpx_mode = 11;280 281 if (default_mpx_mode != t->mpxmode) {282 t->mpxmode = default_mpx_mode;283 mpx_setup(t);284 }285 return 0;286}287 288static int sony_btf_mpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)289{290 struct sony_btf_mpx *t = to_state(sd);291 292 vt->capability = V4L2_TUNER_CAP_NORM |293 V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |294 V4L2_TUNER_CAP_LANG2;295 vt->rxsubchans = V4L2_TUNER_SUB_MONO |296 V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 |297 V4L2_TUNER_SUB_LANG2;298 vt->audmode = t->audmode;299 return 0;300}301 302static int sony_btf_mpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt)303{304 struct sony_btf_mpx *t = to_state(sd);305 306 if (vt->type != V4L2_TUNER_ANALOG_TV)307 return -EINVAL;308 309 if (vt->audmode != t->audmode) {310 t->audmode = vt->audmode;311 mpx_setup(t);312 }313 return 0;314}315 316/* --------------------------------------------------------------------------*/317 318static const struct v4l2_subdev_tuner_ops sony_btf_mpx_tuner_ops = {319 .s_tuner = sony_btf_mpx_s_tuner,320 .g_tuner = sony_btf_mpx_g_tuner,321};322 323static const struct v4l2_subdev_video_ops sony_btf_mpx_video_ops = {324 .s_std = sony_btf_mpx_s_std,325};326 327static const struct v4l2_subdev_ops sony_btf_mpx_ops = {328 .tuner = &sony_btf_mpx_tuner_ops,329 .video = &sony_btf_mpx_video_ops,330};331 332/* --------------------------------------------------------------------------*/333 334static int sony_btf_mpx_probe(struct i2c_client *client)335{336 struct sony_btf_mpx *t;337 struct v4l2_subdev *sd;338 339 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))340 return -ENODEV;341 342 v4l_info(client, "chip found @ 0x%x (%s)\n",343 client->addr << 1, client->adapter->name);344 345 t = devm_kzalloc(&client->dev, sizeof(*t), GFP_KERNEL);346 if (t == NULL)347 return -ENOMEM;348 349 sd = &t->sd;350 v4l2_i2c_subdev_init(sd, client, &sony_btf_mpx_ops);351 352 /* Initialize sony_btf_mpx */353 t->mpxmode = 0;354 t->audmode = V4L2_TUNER_MODE_STEREO;355 356 return 0;357}358 359static void sony_btf_mpx_remove(struct i2c_client *client)360{361 struct v4l2_subdev *sd = i2c_get_clientdata(client);362 363 v4l2_device_unregister_subdev(sd);364}365 366/* ----------------------------------------------------------------------- */367 368static const struct i2c_device_id sony_btf_mpx_id[] = {369 { "sony-btf-mpx" },370 { }371};372MODULE_DEVICE_TABLE(i2c, sony_btf_mpx_id);373 374static struct i2c_driver sony_btf_mpx_driver = {375 .driver = {376 .name = "sony-btf-mpx",377 },378 .probe = sony_btf_mpx_probe,379 .remove = sony_btf_mpx_remove,380 .id_table = sony_btf_mpx_id,381};382module_i2c_driver(sony_btf_mpx_driver);383