@@ -81,3 +81,6 @@
tuner=81 - Partsnic (Daewoo) PTI-5NF05
tuner=82 - Philips CU1216L
tuner=83 - NXP TDA18271
+tuner=84 - Sony PAL+SECAM (BTF-PG472Z)
+tuner=85 - Sony NTSC_JP (BTF-PK467Z)
+tuner=86 - Sony NTSC (BTF-PB463Z)
@@ -179,4 +179,12 @@
help
A driver for the silicon tuner MAX2165 from Maxim.
+config MEDIA_TUNER_SONY
+ tristate "Sony TV tuner"
+ depends on VIDEO_MEDIA && I2C
+ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A driver for the Sony tuners BTF-PG472Z, BTF-PK467Z, BTF-PB463Z.
+
+
endif # MEDIA_TUNER_CUSTOMISE
@@ -24,6 +24,7 @@
obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
+obj-$(CONFIG_MEDIA_TUNER_SONY) += sony-tuner.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
@@ -0,0 +1,695 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-i2c-drv.h>
+
+MODULE_DESCRIPTION("Sony TV Tuner driver");
+MODULE_LICENSE("GPL v2");
+
+/* #define MPX_DEBUG */
+
+/* AS(IF/MPX) pin: LOW HIGH/OPEN
+ * IF/MPX address: 0x42/0x40 0x43/0x44
+ */
+#define IF_I2C_ADDR 0x43
+#define MPX_I2C_ADDR 0x44
+
+static v4l2_std_id force_band;
+static char force_band_str[] = "-";
+module_param_string(force_band, force_band_str, sizeof(force_band_str), 0644);
+static int force_mpx_mode = -1;
+module_param(force_mpx_mode, int, 0644);
+
+/* Store tuner info in the same format as tuner.c, so maybe we can put the
+ * Sony tuner support in there. */
+struct sony_tunertype {
+ char *name;
+ unsigned char Vendor; /* unused here */
+ unsigned char Type; /* unused here */
+
+ unsigned short thresh1; /* band switch VHF_LO <=> VHF_HI */
+ unsigned short thresh2; /* band switch VHF_HI <=> UHF */
+ unsigned char VHF_L;
+ unsigned char VHF_H;
+ unsigned char UHF;
+ unsigned char config;
+ unsigned short IFPCoff;
+};
+
+/* This array is indexed by (tuner_type - TUNER_SONY_BTF_PG472Z) */
+static struct sony_tunertype sony_tuners[] = {
+ { "Sony PAL+SECAM (BTF-PG472Z)", 0, 0,
+ 16*144.25, 16*427.25, 0x01, 0x02, 0x04, 0xc6, 623},
+ { "Sony NTSC_JP (BTF-PK467Z)", 0, 0,
+ 16*220.25, 16*467.25, 0x01, 0x02, 0x04, 0xc6, 940},
+ { "Sony NTSC (BTF-PB463Z)", 0, 0,
+ 16*130.25, 16*364.25, 0x01, 0x02, 0x04, 0xc6, 732},
+};
+
+struct sony_tuner {
+ struct v4l2_subdev sd;
+ int type;
+ v4l2_std_id std;
+ unsigned int freq;
+ int mpxmode;
+ u32 audmode;
+};
+
+static inline struct sony_tuner *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct sony_tuner, sd);
+}
+
+/* Basically the same as default_set_tv_freq() in tuner.c */
+static int set_freq(struct i2c_client *client, int freq)
+{
+ struct sony_tuner *t = to_state(i2c_get_clientdata(client));
+ char *band_name;
+ int n;
+ int band_select;
+ struct sony_tunertype *tun;
+ u8 buffer[4];
+
+ tun = &sony_tuners[t->type - TUNER_SONY_BTF_PG472Z];
+ if (freq < tun->thresh1) {
+ band_name = "VHF_L";
+ band_select = tun->VHF_L;
+ } else if (freq < tun->thresh2) {
+ band_name = "VHF_H";
+ band_select = tun->VHF_H;
+ } else {
+ band_name = "UHF";
+ band_select = tun->UHF;
+ }
+ v4l2_info(client, "tuning to frequency %d.%04d (%s)\n",
+ freq / 16, (freq % 16) * 625, band_name);
+ n = freq + tun->IFPCoff;
+
+ buffer[0] = n >> 8;
+ buffer[1] = n & 0xff;
+ buffer[2] = tun->config;
+ buffer[3] = band_select;
+ i2c_master_send(client, buffer, 4);
+
+ return 0;
+}
+
+static int mpx_write(struct i2c_client *client, int dev, int addr, int val)
+{
+ u8 buffer[5];
+ struct i2c_msg msg;
+
+ buffer[0] = dev;
+ buffer[1] = addr >> 8;
+ buffer[2] = addr & 0xff;
+ buffer[3] = val >> 8;
+ buffer[4] = val & 0xff;
+ msg.addr = MPX_I2C_ADDR;
+ msg.flags = 0;
+ msg.len = 5;
+ msg.buf = buffer;
+ i2c_transfer(client->adapter, &msg, 1);
+ return 0;
+}
+
+/*
+ * MPX register values for the BTF-PG472Z:
+ *
+ * FM_ NICAM_ SCART_
+ * MODUS SOURCE ACB PRESCAL PRESCAL PRESCAL SYSTEM VOLUME
+ * 10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000
+ * ---------------------------------------------------------------
+ * Auto 1003 0020 0100 2603 5000 XXXX 0001 7500
+ *
+ * B/G
+ * Mono 1003 0020 0100 2603 5000 XXXX 0003 7500
+ * A2 1003 0020 0100 2601 5000 XXXX 0003 7500
+ * NICAM 1003 0120 0100 2603 5000 XXXX 0008 7500
+ *
+ * I
+ * Mono 1003 0020 0100 2603 7900 XXXX 000A 7500
+ * NICAM 1003 0120 0100 2603 7900 XXXX 000A 7500
+ *
+ * D/K
+ * Mono 1003 0020 0100 2603 5000 XXXX 0004 7500
+ * A2-1 1003 0020 0100 2601 5000 XXXX 0004 7500
+ * A2-2 1003 0020 0100 2601 5000 XXXX 0005 7500
+ * A2-3 1003 0020 0100 2601 5000 XXXX 0007 7500
+ * NICAM 1003 0120 0100 2603 5000 XXXX 000B 7500
+ *
+ * L/L'
+ * Mono 0003 0200 0100 7C03 5000 2200 0009 7500
+ * NICAM 0003 0120 0100 7C03 5000 XXXX 0009 7500
+ *
+ * M
+ * Mono 1003 0200 0100 2B03 5000 2B00 0002 7500
+ *
+ * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX.
+ *
+ * Bilingual selection in A2/NICAM:
+ *
+ * High byte of SOURCE Left chan Right chan
+ * 0x01 MAIN SUB
+ * 0x03 MAIN MAIN
+ * 0x04 SUB SUB
+ *
+ * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or
+ * 0x00 (all other bands). Force mono in A2 with FMONO_A2:
+ *
+ * FMONO_A2
+ * 10/0022
+ * --------
+ * Forced mono ON 07F0
+ * Forced mono OFF 0190
+ */
+
+static struct {
+ enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode;
+ u16 modus;
+ u16 source;
+ u16 acb;
+ u16 fm_prescale;
+ u16 nicam_prescale;
+ u16 scart_prescale;
+ u16 system;
+ u16 volume;
+} mpx_audio_modes[] = {
+ /* Auto */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
+ 0x5000, 0x0000, 0x0001, 0x7500 },
+ /* B/G Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
+ 0x5000, 0x0000, 0x0003, 0x7500 },
+ /* B/G A2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
+ 0x5000, 0x0000, 0x0003, 0x7500 },
+ /* B/G NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
+ 0x5000, 0x0000, 0x0008, 0x7500 },
+ /* I Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
+ 0x7900, 0x0000, 0x000A, 0x7500 },
+ /* I NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
+ 0x7900, 0x0000, 0x000A, 0x7500 },
+ /* D/K Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603,
+ 0x5000, 0x0000, 0x0004, 0x7500 },
+ /* D/K A2-1 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
+ 0x5000, 0x0000, 0x0004, 0x7500 },
+ /* D/K A2-2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
+ 0x5000, 0x0000, 0x0005, 0x7500 },
+ /* D/K A2-3 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601,
+ 0x5000, 0x0000, 0x0007, 0x7500 },
+ /* D/K NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603,
+ 0x5000, 0x0000, 0x000B, 0x7500 },
+ /* L/L' Mono */ { AUD_MONO, 0x0003, 0x0200, 0x0100, 0x7C03,
+ 0x5000, 0x2200, 0x0009, 0x7500 },
+ /* L/L' NICAM */{ AUD_NICAM_L, 0x0003, 0x0120, 0x0100, 0x7C03,
+ 0x5000, 0x0000, 0x0009, 0x7500 },
+};
+
+#define MPX_NUM_MODES ARRAY_SIZE(mpx_audio_modes)
+
+static int mpx_setup(struct i2c_client *client)
+{
+ struct sony_tuner *t = i2c_get_clientdata(client);
+ u16 source = 0;
+ u8 buffer[3];
+ struct i2c_msg msg;
+
+ /* reset MPX */
+ buffer[0] = 0x00;
+ buffer[1] = 0x80;
+ buffer[2] = 0x00;
+ msg.addr = MPX_I2C_ADDR;
+ msg.flags = 0;
+ msg.len = 3;
+ msg.buf = buffer;
+ i2c_transfer(client->adapter, &msg, 1);
+ buffer[1] = 0x00;
+ i2c_transfer(client->adapter, &msg, 1);
+
+ if (mpx_audio_modes[t->mpxmode].audio_mode != AUD_MONO) {
+ switch (t->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ switch (mpx_audio_modes[t->mpxmode].audio_mode) {
+ case AUD_A2:
+ source = mpx_audio_modes[t->mpxmode].source;
+ break;
+ case AUD_NICAM:
+ source = 0x0000;
+ break;
+ case AUD_NICAM_L:
+ source = 0x0200;
+ break;
+ default:
+ break;
+ }
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ source = mpx_audio_modes[t->mpxmode].source;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ source = 0x0300;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ source = 0x0400;
+ break;
+ }
+ source |= mpx_audio_modes[t->mpxmode].source & 0x00ff;
+ } else
+ source = mpx_audio_modes[t->mpxmode].source;
+
+ mpx_write(client, 0x10, 0x0030, mpx_audio_modes[t->mpxmode].modus);
+ mpx_write(client, 0x12, 0x0008, source);
+ mpx_write(client, 0x12, 0x0013, mpx_audio_modes[t->mpxmode].acb);
+ mpx_write(client, 0x12, 0x000e,
+ mpx_audio_modes[t->mpxmode].fm_prescale);
+ mpx_write(client, 0x12, 0x0010,
+ mpx_audio_modes[t->mpxmode].nicam_prescale);
+ mpx_write(client, 0x12, 0x000d,
+ mpx_audio_modes[t->mpxmode].scart_prescale);
+ mpx_write(client, 0x10, 0x0020, mpx_audio_modes[t->mpxmode].system);
+ mpx_write(client, 0x12, 0x0000, mpx_audio_modes[t->mpxmode].volume);
+ if (mpx_audio_modes[t->mpxmode].audio_mode == AUD_A2)
+ mpx_write(client, 0x10, 0x0022,
+ t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190);
+
+#ifdef MPX_DEBUG
+ {
+ u8 buf1[3], buf2[2];
+ struct i2c_msg msgs[2];
+
+ v4l2_info(client, "MPX registers: %04x %04x "
+ "%04x %04x %04x %04x %04x %04x\n",
+ mpx_audio_modes[t->mpxmode].modus,
+ source,
+ mpx_audio_modes[t->mpxmode].acb,
+ mpx_audio_modes[t->mpxmode].fm_prescale,
+ mpx_audio_modes[t->mpxmode].nicam_prescale,
+ mpx_audio_modes[t->mpxmode].scart_prescale,
+ mpx_audio_modes[t->mpxmode].system,
+ mpx_audio_modes[t->mpxmode].volume);
+ buf1[0] = 0x11;
+ buf1[1] = 0x00;
+ buf1[2] = 0x7e;
+ msgs[0].addr = MPX_I2C_ADDR;
+ msgs[0].flags = 0;
+ msgs[0].len = 3;
+ msgs[0].buf = buf1;
+ msgs[1].addr = MPX_I2C_ADDR;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 2;
+ msgs[1].buf = buf2;
+ i2c_transfer(client->adapter, msgs, 2);
+ v4l2_info(client, "MPX system: %02x%02x\n",
+ buf2[0], buf2[1]);
+ buf1[0] = 0x11;
+ buf1[1] = 0x02;
+ buf1[2] = 0x00;
+ i2c_transfer(client->adapter, msgs, 2);
+ v4l2_info(client, "MPX status: %02x%02x\n",
+ buf2[0], buf2[1]);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * IF configuration values for the BTF-PG472Z:
+ *
+ * B/G: 0x94 0x70 0x49
+ * I: 0x14 0x70 0x4a
+ * D/K: 0x14 0x70 0x4b
+ * L: 0x04 0x70 0x4b
+ * L': 0x44 0x70 0x53
+ * M: 0x50 0x30 0x4c
+ */
+
+static int set_if(struct i2c_client *client)
+{
+ struct sony_tuner *t = i2c_get_clientdata(client);
+ u8 buffer[4];
+ struct i2c_msg msg;
+ int default_mpx_mode = 0;
+
+ /* configure IF */
+ buffer[0] = 0;
+ if (t->std & V4L2_STD_PAL_BG) {
+ buffer[1] = 0x94;
+ buffer[2] = 0x70;
+ buffer[3] = 0x49;
+ default_mpx_mode = 1;
+ } else if (t->std & V4L2_STD_PAL_I) {
+ buffer[1] = 0x14;
+ buffer[2] = 0x70;
+ buffer[3] = 0x4a;
+ default_mpx_mode = 4;
+ } else if (t->std & V4L2_STD_PAL_DK) {
+ buffer[1] = 0x14;
+ buffer[2] = 0x70;
+ buffer[3] = 0x4b;
+ default_mpx_mode = 6;
+ } else if (t->std & V4L2_STD_SECAM_L) {
+ buffer[1] = 0x04;
+ buffer[2] = 0x70;
+ buffer[3] = 0x4b;
+ default_mpx_mode = 11;
+ }
+ msg.addr = IF_I2C_ADDR;
+ msg.flags = 0;
+ msg.len = 4;
+ msg.buf = buffer;
+ i2c_transfer(client->adapter, &msg, 1);
+
+ /* Select MPX mode if not forced by the user */
+ if (force_mpx_mode >= 0 && force_mpx_mode < MPX_NUM_MODES)
+ t->mpxmode = force_mpx_mode;
+ else
+ t->mpxmode = default_mpx_mode;
+ v4l2_info(client, "setting MPX to mode %d\n", t->mpxmode);
+ mpx_setup(client);
+
+ return 0;
+}
+
+static int sony_tuner_s_mode(struct v4l2_subdev *sd,
+ enum v4l2_tuner_type type)
+{
+ struct sony_tuner *t = to_state(sd);
+
+ if (t->type >= 0) {
+ if (t->type != type)
+ v4l2_err(sd, "type already set to %d, "
+ "ignoring request for %d\n",
+ t->type, type);
+ return 0;
+ }
+ t->type = type;
+ switch (t->type) {
+ case TUNER_SONY_BTF_PG472Z:
+ switch (force_band_str[0]) {
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ v4l2_info(sd, "forcing tuner to PAL-B/G bands\n");
+ force_band = V4L2_STD_PAL_BG;
+ break;
+ case 'i':
+ case 'I':
+ v4l2_info(sd, "forcing tuner to PAL-I band\n");
+ force_band = V4L2_STD_PAL_I;
+ break;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ v4l2_info(sd, "forcing tuner to PAL-D/K bands\n");
+ force_band = V4L2_STD_PAL_I;
+ break;
+ case 'l':
+ case 'L':
+ v4l2_info(sd, "forcing tuner to SECAM-L band\n");
+ force_band = V4L2_STD_SECAM_L;
+ break;
+ default:
+ force_band = 0;
+ break;
+ }
+ if (force_band)
+ t->std = force_band;
+ else
+ t->std = V4L2_STD_PAL_BG;
+ set_if(v4l2_get_subdevdata(sd));
+ break;
+ case TUNER_SONY_BTF_PK467Z:
+ t->std = V4L2_STD_NTSC_M_JP;
+ break;
+ case TUNER_SONY_BTF_PB463Z:
+ t->std = V4L2_STD_NTSC_M;
+ break;
+ default:
+ v4l2_err(sd, "tuner type %d is not supported by this module\n",
+ type);
+ return -EINVAL;
+ }
+ if (type >= 0)
+ v4l2_info(sd, "type set to %d (%s)\n",
+ t->type,
+ sony_tuners[t->type - TUNER_SONY_BTF_PG472Z].name);
+ return 0;
+}
+
+static int sony_tuner_s_type_addr(struct v4l2_subdev *sd,
+ struct tuner_setup *type)
+{
+ return sony_tuner_s_mode(sd, type->type);
+}
+
+static int sony_tuner_g_frequency(struct v4l2_subdev *sd,
+ struct v4l2_frequency *freq)
+{
+ struct sony_tuner *t = to_state(sd);
+
+ freq->frequency = t->freq;
+ return 0;
+}
+
+static int sony_tuner_s_frequency(struct v4l2_subdev *sd,
+ struct v4l2_frequency *freq)
+{
+ struct sony_tuner *t = to_state(sd);
+
+ t->freq = freq->frequency;
+ set_freq(v4l2_get_subdevdata(sd), t->freq);
+ return 0;
+}
+
+
+
+static int sony_tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct sony_tuner *t = to_state(sd);
+ v4l2_std_id old = t->std;
+
+ switch (t->type) {
+ case TUNER_SONY_BTF_PG472Z:
+ if (force_band && (norm & force_band) != norm &&
+ norm != V4L2_STD_PAL &&
+ norm != V4L2_STD_SECAM) {
+ v4l2_info(sd, "ignoring requested TV standard in "
+ "favor of force_band value\n");
+ t->std = force_band;
+ } else if (norm & V4L2_STD_PAL_BG) { /* default */
+ t->std = V4L2_STD_PAL_BG;
+ } else if (norm & V4L2_STD_PAL_I) {
+ t->std = V4L2_STD_PAL_I;
+ } else if (norm & V4L2_STD_PAL_DK) {
+ t->std = V4L2_STD_PAL_DK;
+ } else if (norm & V4L2_STD_SECAM_L) {
+ t->std = V4L2_STD_SECAM_L;
+ } else {
+ v4l2_err(sd, "TV standard not supported\n");
+ return -EINVAL;
+ }
+ if (old != t->std)
+ set_if(v4l2_get_subdevdata(sd));
+ break;
+ case TUNER_SONY_BTF_PK467Z:
+ if (!(norm & V4L2_STD_NTSC_M_JP)) {
+ v4l2_err(sd, "TV standard not supported\n");
+ return -EINVAL;
+ }
+ break;
+ case TUNER_SONY_BTF_PB463Z:
+ if (!(norm & V4L2_STD_NTSC_M)) {
+ v4l2_err(sd, "TV standard not supported\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int sony_tuner_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct sony_tuner *t = to_state(sd);
+
+ switch (t->type) {
+ case TUNER_SONY_BTF_PG472Z:
+ if (force_band)
+ *std = force_band;
+ else
+ *std = V4L2_STD_PAL_BG | V4L2_STD_PAL_I |
+ V4L2_STD_PAL_DK | V4L2_STD_SECAM_L;
+ break;
+ case TUNER_SONY_BTF_PK467Z:
+ *std = V4L2_STD_NTSC_M_JP;
+ break;
+ case TUNER_SONY_BTF_PB463Z:
+ *std = V4L2_STD_NTSC_M;
+ break;
+ }
+ return 0;
+}
+
+static int sony_tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct sony_tuner *t = to_state(sd);
+
+ memset(vt, 0, sizeof(*vt));
+ strcpy(vt->name, "Television");
+ vt->type = V4L2_TUNER_ANALOG_TV;
+ vt->rangelow = 0UL; /* does anything use these? */
+ vt->rangehigh = 0xffffffffUL;
+ switch (t->type) {
+ case TUNER_SONY_BTF_PG472Z:
+ vt->capability = V4L2_TUNER_CAP_NORM |
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2;
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO |
+ V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2;
+ break;
+ case TUNER_SONY_BTF_PK467Z:
+ case TUNER_SONY_BTF_PB463Z:
+ vt->capability = V4L2_TUNER_CAP_STEREO;
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO |
+ V4L2_TUNER_SUB_STEREO;
+ break;
+ }
+ vt->audmode = t->audmode;
+ return 0;
+}
+
+static int sony_tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct sony_tuner *t = to_state(sd);
+
+ switch (t->type) {
+ case TUNER_SONY_BTF_PG472Z:
+ if (vt->audmode != t->audmode) {
+ t->audmode = vt->audmode;
+ mpx_setup(v4l2_get_subdevdata(sd));
+ }
+ break;
+ case TUNER_SONY_BTF_PK467Z:
+ case TUNER_SONY_BTF_PB463Z:
+ break;
+ }
+ return 0;
+}
+
+static int sony_tuner_log_status(struct v4l2_subdev *sd)
+{
+ struct sony_tuner *t = to_state(sd);
+
+ v4l2_info(sd, "Standard: %s\n", t->std == V4L2_STD_NTSC ? "NTSC" :
+ t->std == V4L2_STD_PAL ? "PAL" :
+ t->std == V4L2_STD_SECAM ? "SECAM" :
+ "unknown");
+ v4l2_info(sd, "Frequency: %ud\n", t->freq);
+ return 0;
+}
+
+/* --------------------------------------------------------------------------*/
+
+static const struct v4l2_subdev_core_ops sony_tuner_core_ops = {
+ .log_status = sony_tuner_log_status,
+ .s_std = sony_tuner_s_std,
+};
+
+static const struct v4l2_subdev_tuner_ops sony_tuner_tuner_ops = {
+ .s_mode = sony_tuner_s_mode,
+ .s_frequency = sony_tuner_s_frequency,
+ .g_frequency = sony_tuner_g_frequency,
+ .s_tuner = sony_tuner_s_tuner,
+ .g_tuner = sony_tuner_g_tuner,
+ .s_type_addr = sony_tuner_s_type_addr,
+};
+
+static const struct v4l2_subdev_video_ops sony_tuner_video_ops = {
+ .querystd = sony_tuner_querystd,
+};
+
+static const struct v4l2_subdev_ops sony_tuner_ops = {
+ .core = &sony_tuner_core_ops,
+ .tuner = &sony_tuner_tuner_ops,
+ .video = &sony_tuner_video_ops,
+};
+
+/* --------------------------------------------------------------------------*/
+
+static int sony_tuner_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct sony_tuner *t;
+ struct v4l2_subdev *sd;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -ENODEV;
+
+ v4l2_info(client, "initializing Sony TV Tuner at address 0x%x on %s\n",
+ client->addr, client->adapter->name);
+
+ t = kmalloc(sizeof(struct sony_tuner), GFP_KERNEL);
+ if (t == NULL)
+ return -ENOMEM;
+
+ sd = &t->sd;
+ v4l2_i2c_subdev_init(sd, client, &sony_tuner_ops);
+
+ /* Initialize sony_tuner */
+ t->type = -1;
+ t->freq = 0;
+ t->mpxmode = 0;
+ t->audmode = V4L2_TUNER_MODE_STEREO;
+
+ return 0;
+}
+
+static int sony_tuner_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+static const struct i2c_device_id sony_tuner_id[] = {
+ { "sony_tuner", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sony_tuner_id);
+#endif
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "sony_tuner",
+ .probe = sony_tuner_probe,
+ .remove = sony_tuner_remove,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+ .id_table = sony_tuner_id,
+#endif
+};
@@ -1806,6 +1806,18 @@
.name = "NXP TDA18271",
/* see tda18271-fe.c for details */
},
+ [TUNER_SONY_BTF_PG472Z] = {
+ .name = "Sony PAL+SECAM (BTF-PG472Z)",
+ /* see sony-tuner.c for details */
+ },
+ [TUNER_SONY_BTF_PK467Z] = {
+ .name = "Sony NTSC_JP (BTF-PK467Z)",
+ /* see sony-tuner.c for details */
+ },
+ [TUNER_SONY_BTF_PB463Z] = {
+ .name = "Sony NTSC (BTF-PB463Z)",
+ /* see sony-tuner.c for details */
+ },
};
EXPORT_SYMBOL(tuners);
@@ -292,9 +292,3 @@
/* We re-use the I2C_M_TEN value so the flag passes through the masks in the
* core I2C code. Major kludge, but the I2C layer ain't exactly flexible. */
#define I2C_CLIENT_SCCB 0x10
-
-/* Sony tuner types */
-
-#define TUNER_SONY_BTF_PG472Z 200
-#define TUNER_SONY_BTF_PK467Z 201
-#define TUNER_SONY_BTF_PB463Z 202
@@ -27,6 +27,7 @@
#include <linux/i2c.h>
#include <asm/byteorder.h>
#include <media/tvaudio.h>
+#include <media/tuner.h>
#include "go7007-priv.h"
@@ -217,7 +218,7 @@
.addr = 0x1a,
},
{
- .type = "wis_sony_tuner",
+ .type = "sony_tuner",
.addr = 0x60,
},
},
@@ -130,6 +130,10 @@
#define TUNER_PHILIPS_CU1216L 82
#define TUNER_NXP_TDA18271 83
+#define TUNER_SONY_BTF_PG472Z 84 /* PAL+SECAM */
+#define TUNER_SONY_BTF_PK467Z 85 /* NTSC_JP */
+#define TUNER_SONY_BTF_PB463Z 86 /* NTSC */
+
/* tv card specific */
#define TDA9887_PRESENT (1<<0)
#define TDA9887_PORT1_INACTIVE (1<<1)