From patchwork Fri Feb 12 00:33:07 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pete Eberlein X-Patchwork-Id: 78831 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o1C60IkM011845 for ; Fri, 12 Feb 2010 06:00:18 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751419Ab0BLGAJ (ORCPT ); Fri, 12 Feb 2010 01:00:09 -0500 Received: from gateway14.websitewelcome.com ([69.93.82.2]:35356 "HELO gateway14.websitewelcome.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with SMTP id S1751396Ab0BLGAH (ORCPT ); Fri, 12 Feb 2010 01:00:07 -0500 X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Fri, 12 Feb 2010 06:00:20 +0000 (UTC) X-Greylist: delayed 19585 seconds by postgrey-1.27 at vger.kernel.org; Fri, 12 Feb 2010 01:00:07 EST Received: (qmail 11515 invoked from network); 12 Feb 2010 00:35:22 -0000 Received: from gator886.hostgator.com (174.120.40.226) by gateway14.websitewelcome.com with SMTP; 12 Feb 2010 00:35:22 -0000 Received: from [66.15.212.169] (port=10319 helo=[10.140.5.12]) by gator886.hostgator.com with esmtpsa (SSLv3:AES256-SHA:256) (Exim 4.69) (envelope-from ) id 1NfjTI-0005ky-LX for linux-media@vger.kernel.org; Thu, 11 Feb 2010 18:33:25 -0600 Subject: [PATCH 2/5] sony-tuner: Subdev conversion from wis-sony-tuner From: Pete Eberlein To: "linux-media@vger.kernel.org" Date: Thu, 11 Feb 2010 16:33:07 -0800 Message-Id: <1265934787.4626.251.camel@pete-desktop> Mime-Version: 1.0 X-Mailer: Evolution 2.26.1 X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - gator886.hostgator.com X-AntiAbuse: Original Domain - vger.kernel.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - sensoray.com Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org diff -r 2d2a250ca33b -r 628119533574 linux/Documentation/video4linux/CARDLIST.tuner --- a/linux/Documentation/video4linux/CARDLIST.tuner Wed Feb 10 11:25:59 2010 -0800 +++ b/linux/Documentation/video4linux/CARDLIST.tuner Thu Feb 11 15:21:11 2010 -0800 @@ -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) diff -r 2d2a250ca33b -r 628119533574 linux/drivers/media/common/tuners/Kconfig --- a/linux/drivers/media/common/tuners/Kconfig Wed Feb 10 11:25:59 2010 -0800 +++ b/linux/drivers/media/common/tuners/Kconfig Thu Feb 11 15:21:11 2010 -0800 @@ -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 diff -r 2d2a250ca33b -r 628119533574 linux/drivers/media/common/tuners/Makefile --- a/linux/drivers/media/common/tuners/Makefile Wed Feb 10 11:25:59 2010 -0800 +++ b/linux/drivers/media/common/tuners/Makefile Thu Feb 11 15:21:11 2010 -0800 @@ -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 diff -r 2d2a250ca33b -r 628119533574 linux/drivers/media/common/tuners/sony-tuner.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/common/tuners/sony-tuner.c Thu Feb 11 15:21:11 2010 -0800 @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 +}; diff -r 2d2a250ca33b -r 628119533574 linux/drivers/media/common/tuners/tuner-types.c --- a/linux/drivers/media/common/tuners/tuner-types.c Wed Feb 10 11:25:59 2010 -0800 +++ b/linux/drivers/media/common/tuners/tuner-types.c Thu Feb 11 15:21:11 2010 -0800 @@ -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); diff -r 2d2a250ca33b -r 628119533574 linux/drivers/staging/go7007/go7007-priv.h --- a/linux/drivers/staging/go7007/go7007-priv.h Wed Feb 10 11:25:59 2010 -0800 +++ b/linux/drivers/staging/go7007/go7007-priv.h Thu Feb 11 15:21:11 2010 -0800 @@ -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 diff -r 2d2a250ca33b -r 628119533574 linux/drivers/staging/go7007/go7007-usb.c --- a/linux/drivers/staging/go7007/go7007-usb.c Wed Feb 10 11:25:59 2010 -0800 +++ b/linux/drivers/staging/go7007/go7007-usb.c Thu Feb 11 15:21:11 2010 -0800 @@ -27,6 +27,7 @@ #include #include #include +#include #include "go7007-priv.h" @@ -217,7 +218,7 @@ .addr = 0x1a, }, { - .type = "wis_sony_tuner", + .type = "sony_tuner", .addr = 0x60, }, }, diff -r 2d2a250ca33b -r 628119533574 linux/include/media/tuner.h --- a/linux/include/media/tuner.h Wed Feb 10 11:25:59 2010 -0800 +++ b/linux/include/media/tuner.h Thu Feb 11 15:21:11 2010 -0800 @@ -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)