Path: blob/master/drivers/media/dvb/frontends/au8522_decoder.c
15112 views
/*1* Auvitek AU8522 QAM/8VSB demodulator driver and video decoder2*3* Copyright (C) 2009 Devin Heitmueller <[email protected]>4* Copyright (C) 2005-2008 Auvitek International, Ltd.5*6* This program is free software; you can redistribute it and/or7* modify it under the terms of the GNU General Public License8* As published by the Free Software Foundation; either version 29* of the License, or (at your option) any later version.10*11* This program is distributed in the hope that it will be useful,12* but WITHOUT ANY WARRANTY; without even the implied warranty of13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14* GNU General Public License for more details.15*16* You should have received a copy of the GNU General Public License17* along with this program; if not, write to the Free Software18* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA19* 02110-1301, USA.20*/2122/* Developer notes:23*24* VBI support is not yet working25* Enough is implemented here for CVBS and S-Video inputs, but the actual26* analog demodulator code isn't implemented (not needed for xc5000 since it27* has its own demodulator and outputs CVBS)28*29*/3031#include <linux/kernel.h>32#include <linux/slab.h>33#include <linux/videodev2.h>34#include <linux/i2c.h>35#include <linux/delay.h>36#include <media/v4l2-common.h>37#include <media/v4l2-chip-ident.h>38#include <media/v4l2-device.h>39#include "au8522.h"40#include "au8522_priv.h"4142MODULE_AUTHOR("Devin Heitmueller");43MODULE_LICENSE("GPL");4445static int au8522_analog_debug;464748module_param_named(analog_debug, au8522_analog_debug, int, 0644);4950MODULE_PARM_DESC(analog_debug,51"Analog debugging messages [0=Off (default) 1=On]");5253struct au8522_register_config {54u16 reg_name;55u8 reg_val[8];56};575859/* Video Decoder Filter Coefficients60The values are as follows from left to right610="ATV RF" 1="ATV RF13" 2="CVBS" 3="S-Video" 4="PAL" 5=CVBS13" 6="SVideo13"62*/63static const struct au8522_register_config filter_coef[] = {64{AU8522_FILTER_COEF_R410, {0x25, 0x00, 0x25, 0x25, 0x00, 0x00, 0x00} },65{AU8522_FILTER_COEF_R411, {0x20, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00} },66{AU8522_FILTER_COEF_R412, {0x03, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00} },67{AU8522_FILTER_COEF_R413, {0xe6, 0x00, 0xe6, 0xe6, 0x00, 0x00, 0x00} },68{AU8522_FILTER_COEF_R414, {0x40, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00} },69{AU8522_FILTER_COEF_R415, {0x1b, 0x00, 0x1b, 0x1b, 0x00, 0x00, 0x00} },70{AU8522_FILTER_COEF_R416, {0xc0, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00} },71{AU8522_FILTER_COEF_R417, {0x04, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00} },72{AU8522_FILTER_COEF_R418, {0x8c, 0x00, 0x8c, 0x8c, 0x00, 0x00, 0x00} },73{AU8522_FILTER_COEF_R419, {0xa0, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0x40} },74{AU8522_FILTER_COEF_R41A, {0x21, 0x09, 0x21, 0x21, 0x09, 0x09, 0x09} },75{AU8522_FILTER_COEF_R41B, {0x6c, 0x38, 0x6c, 0x6c, 0x38, 0x38, 0x38} },76{AU8522_FILTER_COEF_R41C, {0x03, 0xff, 0x03, 0x03, 0xff, 0xff, 0xff} },77{AU8522_FILTER_COEF_R41D, {0xbf, 0xc7, 0xbf, 0xbf, 0xc7, 0xc7, 0xc7} },78{AU8522_FILTER_COEF_R41E, {0xa0, 0xdf, 0xa0, 0xa0, 0xdf, 0xdf, 0xdf} },79{AU8522_FILTER_COEF_R41F, {0x10, 0x06, 0x10, 0x10, 0x06, 0x06, 0x06} },80{AU8522_FILTER_COEF_R420, {0xae, 0x30, 0xae, 0xae, 0x30, 0x30, 0x30} },81{AU8522_FILTER_COEF_R421, {0xc4, 0x01, 0xc4, 0xc4, 0x01, 0x01, 0x01} },82{AU8522_FILTER_COEF_R422, {0x54, 0xdd, 0x54, 0x54, 0xdd, 0xdd, 0xdd} },83{AU8522_FILTER_COEF_R423, {0xd0, 0xaf, 0xd0, 0xd0, 0xaf, 0xaf, 0xaf} },84{AU8522_FILTER_COEF_R424, {0x1c, 0xf7, 0x1c, 0x1c, 0xf7, 0xf7, 0xf7} },85{AU8522_FILTER_COEF_R425, {0x76, 0xdb, 0x76, 0x76, 0xdb, 0xdb, 0xdb} },86{AU8522_FILTER_COEF_R426, {0x61, 0xc0, 0x61, 0x61, 0xc0, 0xc0, 0xc0} },87{AU8522_FILTER_COEF_R427, {0xd1, 0x2f, 0xd1, 0xd1, 0x2f, 0x2f, 0x2f} },88{AU8522_FILTER_COEF_R428, {0x84, 0xd8, 0x84, 0x84, 0xd8, 0xd8, 0xd8} },89{AU8522_FILTER_COEF_R429, {0x06, 0xfb, 0x06, 0x06, 0xfb, 0xfb, 0xfb} },90{AU8522_FILTER_COEF_R42A, {0x21, 0xd5, 0x21, 0x21, 0xd5, 0xd5, 0xd5} },91{AU8522_FILTER_COEF_R42B, {0x0a, 0x3e, 0x0a, 0x0a, 0x3e, 0x3e, 0x3e} },92{AU8522_FILTER_COEF_R42C, {0xe6, 0x15, 0xe6, 0xe6, 0x15, 0x15, 0x15} },93{AU8522_FILTER_COEF_R42D, {0x01, 0x34, 0x01, 0x01, 0x34, 0x34, 0x34} },9495};96#define NUM_FILTER_COEF (sizeof(filter_coef)\97/ sizeof(struct au8522_register_config))9899100/* Registers 0x060b through 0x0652 are the LP Filter coefficients101The values are as follows from left to right1020="SIF" 1="ATVRF/ATVRF13"103Note: the "ATVRF/ATVRF13" mode has never been tested104*/105static const struct au8522_register_config lpfilter_coef[] = {106{0x060b, {0x21, 0x0b} },107{0x060c, {0xad, 0xad} },108{0x060d, {0x70, 0xf0} },109{0x060e, {0xea, 0xe9} },110{0x060f, {0xdd, 0xdd} },111{0x0610, {0x08, 0x64} },112{0x0611, {0x60, 0x60} },113{0x0612, {0xf8, 0xb2} },114{0x0613, {0x01, 0x02} },115{0x0614, {0xe4, 0xb4} },116{0x0615, {0x19, 0x02} },117{0x0616, {0xae, 0x2e} },118{0x0617, {0xee, 0xc5} },119{0x0618, {0x56, 0x56} },120{0x0619, {0x30, 0x58} },121{0x061a, {0xf9, 0xf8} },122{0x061b, {0x24, 0x64} },123{0x061c, {0x07, 0x07} },124{0x061d, {0x30, 0x30} },125{0x061e, {0xa9, 0xed} },126{0x061f, {0x09, 0x0b} },127{0x0620, {0x42, 0xc2} },128{0x0621, {0x1d, 0x2a} },129{0x0622, {0xd6, 0x56} },130{0x0623, {0x95, 0x8b} },131{0x0624, {0x2b, 0x2b} },132{0x0625, {0x30, 0x24} },133{0x0626, {0x3e, 0x3e} },134{0x0627, {0x62, 0xe2} },135{0x0628, {0xe9, 0xf5} },136{0x0629, {0x99, 0x19} },137{0x062a, {0xd4, 0x11} },138{0x062b, {0x03, 0x04} },139{0x062c, {0xb5, 0x85} },140{0x062d, {0x1e, 0x20} },141{0x062e, {0x2a, 0xea} },142{0x062f, {0xd7, 0xd2} },143{0x0630, {0x15, 0x15} },144{0x0631, {0xa3, 0xa9} },145{0x0632, {0x1f, 0x1f} },146{0x0633, {0xf9, 0xd1} },147{0x0634, {0xc0, 0xc3} },148{0x0635, {0x4d, 0x8d} },149{0x0636, {0x21, 0x31} },150{0x0637, {0x83, 0x83} },151{0x0638, {0x08, 0x8c} },152{0x0639, {0x19, 0x19} },153{0x063a, {0x45, 0xa5} },154{0x063b, {0xef, 0xec} },155{0x063c, {0x8a, 0x8a} },156{0x063d, {0xf4, 0xf6} },157{0x063e, {0x8f, 0x8f} },158{0x063f, {0x44, 0x0c} },159{0x0640, {0xef, 0xf0} },160{0x0641, {0x66, 0x66} },161{0x0642, {0xcc, 0xd2} },162{0x0643, {0x41, 0x41} },163{0x0644, {0x63, 0x93} },164{0x0645, {0x8e, 0x8e} },165{0x0646, {0xa2, 0x42} },166{0x0647, {0x7b, 0x7b} },167{0x0648, {0x04, 0x04} },168{0x0649, {0x00, 0x00} },169{0x064a, {0x40, 0x40} },170{0x064b, {0x8c, 0x98} },171{0x064c, {0x00, 0x00} },172{0x064d, {0x63, 0xc3} },173{0x064e, {0x04, 0x04} },174{0x064f, {0x20, 0x20} },175{0x0650, {0x00, 0x00} },176{0x0651, {0x40, 0x40} },177{0x0652, {0x01, 0x01} },178};179#define NUM_LPFILTER_COEF (sizeof(lpfilter_coef)\180/ sizeof(struct au8522_register_config))181182static inline struct au8522_state *to_state(struct v4l2_subdev *sd)183{184return container_of(sd, struct au8522_state, sd);185}186187static void setup_vbi(struct au8522_state *state, int aud_input)188{189int i;190191/* These are set to zero regardless of what mode we're in */192au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H, 0x00);193au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_L_REG018H, 0x00);194au8522_writereg(state, AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H, 0x00);195au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH, 0x00);196au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH, 0x00);197au8522_writereg(state, AU8522_TVDEC_VBI_USER_THRESH1_REG01CH, 0x00);198au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH, 0x00);199au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH, 0x00);200au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H, 0x00);201au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H,2020x00);203au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H,2040x00);205au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H,2060x00);207208/* Setup the VBI registers */209for (i = 0x30; i < 0x60; i++)210au8522_writereg(state, i, 0x40);211212/* For some reason, every register is 0x40 except register 0x44213(confirmed via the HVR-950q USB capture) */214au8522_writereg(state, 0x44, 0x60);215216/* Enable VBI (we always do this regardless of whether the user is217viewing closed caption info) */218au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H,219AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON);220221}222223static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode)224{225int i;226int filter_coef_type;227228/* Provide reasonable defaults for picture tuning values */229au8522_writereg(state, AU8522_TVDEC_SHARPNESSREG009H, 0x07);230au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, 0xed);231state->brightness = 0xed - 128;232au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, 0x79);233state->contrast = 0x79;234au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80);235au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80);236state->saturation = 0x80;237au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00);238au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00);239state->hue = 0x00;240241/* Other decoder registers */242au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00);243244if (input_mode == 0x23) {245/* S-Video input mapping */246au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x04);247} else {248/* All other modes (CVBS/ATVRF etc.) */249au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x00);250}251252au8522_writereg(state, AU8522_TVDEC_PGA_REG012H,253AU8522_TVDEC_PGA_REG012H_CVBS);254au8522_writereg(state, AU8522_TVDEC_COMB_MODE_REG015H,255AU8522_TVDEC_COMB_MODE_REG015H_CVBS);256au8522_writereg(state, AU8522_TVDED_DBG_MODE_REG060H,257AU8522_TVDED_DBG_MODE_REG060H_CVBS);258au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H,259AU8522_TVDEC_FORMAT_CTRL1_REG061H_CVBS13);260au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H,261AU8522_TVDEC_FORMAT_CTRL2_REG062H_CVBS13);262au8522_writereg(state, AU8522_TVDEC_VCR_DET_LLIM_REG063H,263AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS);264au8522_writereg(state, AU8522_TVDEC_VCR_DET_HLIM_REG064H,265AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS);266au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR1_REG065H,267AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS);268au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR2_REG066H,269AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS);270au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR3_REG067H,271AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS);272au8522_writereg(state, AU8522_TVDEC_COMB_NOTCH_THR_REG068H,273AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS);274au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR1_REG069H,275AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS);276au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR2_REG06AH,277AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS);278au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR3_REG06BH,279AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS);280if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 ||281input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) {282au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH,283AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO);284au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH,285AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO);286} else {287au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH,288AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS);289au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH,290AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS);291}292au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH,293AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS);294au8522_writereg(state, AU8522_TVDEC_UV_SEP_THR_REG06FH,295AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS);296au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H,297AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS);298au8522_writereg(state, AU8522_REG071H, AU8522_REG071H_CVBS);299au8522_writereg(state, AU8522_REG072H, AU8522_REG072H_CVBS);300au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H,301AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS);302au8522_writereg(state, AU8522_REG074H, AU8522_REG074H_CVBS);303au8522_writereg(state, AU8522_REG075H, AU8522_REG075H_CVBS);304au8522_writereg(state, AU8522_TVDEC_DCAGC_CTRL_REG077H,305AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS);306au8522_writereg(state, AU8522_TVDEC_PIC_START_ADJ_REG078H,307AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS);308au8522_writereg(state, AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H,309AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS);310au8522_writereg(state, AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH,311AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS);312au8522_writereg(state, AU8522_TVDEC_INTRP_CTRL_REG07BH,313AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS);314au8522_writereg(state, AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H,315AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS);316au8522_writereg(state, AU8522_TOREGAAGC_REG0E5H,317AU8522_TOREGAAGC_REG0E5H_CVBS);318au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS);319320setup_vbi(state, 0);321322if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 ||323input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) {324/* Despite what the table says, for the HVR-950q we still need325to be in CVBS mode for the S-Video input (reason unknown). */326/* filter_coef_type = 3; */327filter_coef_type = 5;328} else {329filter_coef_type = 5;330}331332/* Load the Video Decoder Filter Coefficients */333for (i = 0; i < NUM_FILTER_COEF; i++) {334au8522_writereg(state, filter_coef[i].reg_name,335filter_coef[i].reg_val[filter_coef_type]);336}337338/* It's not clear what these registers are for, but they are always339set to the same value regardless of what mode we're in */340au8522_writereg(state, AU8522_REG42EH, 0x87);341au8522_writereg(state, AU8522_REG42FH, 0xa2);342au8522_writereg(state, AU8522_REG430H, 0xbf);343au8522_writereg(state, AU8522_REG431H, 0xcb);344au8522_writereg(state, AU8522_REG432H, 0xa1);345au8522_writereg(state, AU8522_REG433H, 0x41);346au8522_writereg(state, AU8522_REG434H, 0x88);347au8522_writereg(state, AU8522_REG435H, 0xc2);348au8522_writereg(state, AU8522_REG436H, 0x3c);349}350351static void au8522_setup_cvbs_mode(struct au8522_state *state)352{353/* here we're going to try the pre-programmed route */354au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,355AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS);356357/* PGA in automatic mode */358au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);359360/* Enable clamping control */361au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00);362363au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,364AU8522_INPUT_CONTROL_REG081H_CVBS_CH1);365366setup_decoder_defaults(state, AU8522_INPUT_CONTROL_REG081H_CVBS_CH1);367368au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,369AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);370}371372static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state)373{374/* here we're going to try the pre-programmed route */375au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,376AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS);377378/* It's not clear why we have to have the PGA in automatic mode while379enabling clamp control, but it's what Windows does */380au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);381382/* Enable clamping control */383au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x0e);384385/* Disable automatic PGA (since the CVBS is coming from the tuner) */386au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x10);387388/* Set input mode to CVBS on channel 4 with SIF audio input enabled */389au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,390AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF);391392setup_decoder_defaults(state,393AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF);394395au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,396AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);397}398399static void au8522_setup_svideo_mode(struct au8522_state *state)400{401au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,402AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO);403404/* Set input to Y on Channe1, C on Channel 3 */405au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,406AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13);407408/* PGA in automatic mode */409au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);410411/* Enable clamping control */412au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00);413414setup_decoder_defaults(state,415AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13);416417au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,418AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);419}420421/* ----------------------------------------------------------------------- */422423static void disable_audio_input(struct au8522_state *state)424{425au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00);426au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00);427au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00);428429au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x04);430au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0x02);431432au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,433AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO);434}435436/* 0=disable, 1=SIF */437static void set_audio_input(struct au8522_state *state, int aud_input)438{439int i;440441/* Note that this function needs to be used in conjunction with setting442the input routing via register 0x81 */443444if (aud_input == AU8522_AUDIO_NONE) {445disable_audio_input(state);446return;447}448449if (aud_input != AU8522_AUDIO_SIF) {450/* The caller asked for a mode we don't currently support */451printk(KERN_ERR "Unsupported audio mode requested! mode=%d\n",452aud_input);453return;454}455456/* Load the Audio Decoder Filter Coefficients */457for (i = 0; i < NUM_LPFILTER_COEF; i++) {458au8522_writereg(state, lpfilter_coef[i].reg_name,459lpfilter_coef[i].reg_val[0]);460}461462/* Setup audio */463au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00);464au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00);465au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00);466au8522_writereg(state, AU8522_I2C_CONTROL_REG1_REG091H, 0x80);467au8522_writereg(state, AU8522_I2C_CONTROL_REG0_REG090H, 0x84);468msleep(150);469au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x00);470msleep(1);471au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x9d);472msleep(50);473au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);474au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);475au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0xff);476msleep(80);477au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);478au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);479au8522_writereg(state, AU8522_REG0F9H, AU8522_REG0F9H_AUDIO);480au8522_writereg(state, AU8522_AUDIO_MODE_REG0F1H, 0x82);481msleep(70);482au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09);483au8522_writereg(state, AU8522_AUDIOFREQ_REG606H, 0x03);484au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0xc2);485}486487/* ----------------------------------------------------------------------- */488489static int au8522_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)490{491struct au8522_state *state = to_state(sd);492493switch (ctrl->id) {494case V4L2_CID_BRIGHTNESS:495state->brightness = ctrl->value;496au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH,497ctrl->value - 128);498break;499case V4L2_CID_CONTRAST:500state->contrast = ctrl->value;501au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH,502ctrl->value);503break;504case V4L2_CID_SATURATION:505state->saturation = ctrl->value;506au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH,507ctrl->value);508au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH,509ctrl->value);510break;511case V4L2_CID_HUE:512state->hue = ctrl->value;513au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH,514ctrl->value >> 8);515au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH,516ctrl->value & 0xFF);517break;518case V4L2_CID_AUDIO_VOLUME:519case V4L2_CID_AUDIO_BASS:520case V4L2_CID_AUDIO_TREBLE:521case V4L2_CID_AUDIO_BALANCE:522case V4L2_CID_AUDIO_MUTE:523/* Not yet implemented */524default:525return -EINVAL;526}527528return 0;529}530531static int au8522_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)532{533struct au8522_state *state = to_state(sd);534535/* Note that we are using values cached in the state structure instead536of reading the registers due to issues with i2c reads not working537properly/consistently yet on the HVR-950q */538539switch (ctrl->id) {540case V4L2_CID_BRIGHTNESS:541ctrl->value = state->brightness;542break;543case V4L2_CID_CONTRAST:544ctrl->value = state->contrast;545break;546case V4L2_CID_SATURATION:547ctrl->value = state->saturation;548break;549case V4L2_CID_HUE:550ctrl->value = state->hue;551break;552case V4L2_CID_AUDIO_VOLUME:553case V4L2_CID_AUDIO_BASS:554case V4L2_CID_AUDIO_TREBLE:555case V4L2_CID_AUDIO_BALANCE:556case V4L2_CID_AUDIO_MUTE:557/* Not yet supported */558default:559return -EINVAL;560}561562return 0;563}564565/* ----------------------------------------------------------------------- */566567#ifdef CONFIG_VIDEO_ADV_DEBUG568static int au8522_g_register(struct v4l2_subdev *sd,569struct v4l2_dbg_register *reg)570{571struct i2c_client *client = v4l2_get_subdevdata(sd);572struct au8522_state *state = to_state(sd);573574if (!v4l2_chip_match_i2c_client(client, ®->match))575return -EINVAL;576if (!capable(CAP_SYS_ADMIN))577return -EPERM;578reg->val = au8522_readreg(state, reg->reg & 0xffff);579return 0;580}581582static int au8522_s_register(struct v4l2_subdev *sd,583struct v4l2_dbg_register *reg)584{585struct i2c_client *client = v4l2_get_subdevdata(sd);586struct au8522_state *state = to_state(sd);587588if (!v4l2_chip_match_i2c_client(client, ®->match))589return -EINVAL;590if (!capable(CAP_SYS_ADMIN))591return -EPERM;592au8522_writereg(state, reg->reg, reg->val & 0xff);593return 0;594}595#endif596597static int au8522_s_stream(struct v4l2_subdev *sd, int enable)598{599struct au8522_state *state = to_state(sd);600601if (enable) {602au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,6030x01);604msleep(1);605au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,606AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);607} else {608/* This does not completely power down the device609(it only reduces it from around 140ma to 80ma) */610au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,6111 << 5);612}613return 0;614}615616static int au8522_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)617{618switch (qc->id) {619case V4L2_CID_CONTRAST:620return v4l2_ctrl_query_fill(qc, 0, 255, 1,621AU8522_TVDEC_CONTRAST_REG00BH_CVBS);622case V4L2_CID_BRIGHTNESS:623return v4l2_ctrl_query_fill(qc, 0, 255, 1, 109);624case V4L2_CID_SATURATION:625return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);626case V4L2_CID_HUE:627return v4l2_ctrl_query_fill(qc, -32768, 32768, 1, 0);628default:629break;630}631632qc->type = 0;633return -EINVAL;634}635636static int au8522_reset(struct v4l2_subdev *sd, u32 val)637{638struct au8522_state *state = to_state(sd);639640state->operational_mode = AU8522_ANALOG_MODE;641642/* Clear out any state associated with the digital side of the643chip, so that when it gets powered back up it won't think644that it is already tuned */645state->current_frequency = 0;646647au8522_writereg(state, 0xa4, 1 << 5);648649return 0;650}651652static int au8522_s_video_routing(struct v4l2_subdev *sd,653u32 input, u32 output, u32 config)654{655struct au8522_state *state = to_state(sd);656657au8522_reset(sd, 0);658659/* Jam open the i2c gate to the tuner. We do this here to handle the660case where the user went into digital mode (causing the gate to be661closed), and then came back to analog mode */662au8522_writereg(state, 0x106, 1);663664if (input == AU8522_COMPOSITE_CH1) {665au8522_setup_cvbs_mode(state);666} else if (input == AU8522_SVIDEO_CH13) {667au8522_setup_svideo_mode(state);668} else if (input == AU8522_COMPOSITE_CH4_SIF) {669au8522_setup_cvbs_tuner_mode(state);670} else {671printk(KERN_ERR "au8522 mode not currently supported\n");672return -EINVAL;673}674return 0;675}676677static int au8522_s_audio_routing(struct v4l2_subdev *sd,678u32 input, u32 output, u32 config)679{680struct au8522_state *state = to_state(sd);681set_audio_input(state, input);682return 0;683}684685static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)686{687int val = 0;688struct au8522_state *state = to_state(sd);689u8 lock_status;690691/* Interrogate the decoder to see if we are getting a real signal */692lock_status = au8522_readreg(state, 0x00);693if (lock_status == 0xa2)694vt->signal = 0x01;695else696vt->signal = 0x00;697698vt->capability |=699V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |700V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;701702val = V4L2_TUNER_SUB_MONO;703vt->rxsubchans = val;704vt->audmode = V4L2_TUNER_MODE_STEREO;705return 0;706}707708static int au8522_g_chip_ident(struct v4l2_subdev *sd,709struct v4l2_dbg_chip_ident *chip)710{711struct au8522_state *state = to_state(sd);712struct i2c_client *client = v4l2_get_subdevdata(sd);713714return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev);715}716717static int au8522_log_status(struct v4l2_subdev *sd)718{719/* FIXME: Add some status info here */720return 0;721}722723/* ----------------------------------------------------------------------- */724725static const struct v4l2_subdev_core_ops au8522_core_ops = {726.log_status = au8522_log_status,727.g_chip_ident = au8522_g_chip_ident,728.g_ctrl = au8522_g_ctrl,729.s_ctrl = au8522_s_ctrl,730.queryctrl = au8522_queryctrl,731.reset = au8522_reset,732#ifdef CONFIG_VIDEO_ADV_DEBUG733.g_register = au8522_g_register,734.s_register = au8522_s_register,735#endif736};737738static const struct v4l2_subdev_tuner_ops au8522_tuner_ops = {739.g_tuner = au8522_g_tuner,740};741742static const struct v4l2_subdev_audio_ops au8522_audio_ops = {743.s_routing = au8522_s_audio_routing,744};745746static const struct v4l2_subdev_video_ops au8522_video_ops = {747.s_routing = au8522_s_video_routing,748.s_stream = au8522_s_stream,749};750751static const struct v4l2_subdev_ops au8522_ops = {752.core = &au8522_core_ops,753.tuner = &au8522_tuner_ops,754.audio = &au8522_audio_ops,755.video = &au8522_video_ops,756};757758/* ----------------------------------------------------------------------- */759760static int au8522_probe(struct i2c_client *client,761const struct i2c_device_id *did)762{763struct au8522_state *state;764struct v4l2_subdev *sd;765int instance;766struct au8522_config *demod_config;767768/* Check if the adapter supports the needed features */769if (!i2c_check_functionality(client->adapter,770I2C_FUNC_SMBUS_BYTE_DATA)) {771return -EIO;772}773774/* allocate memory for the internal state */775instance = au8522_get_state(&state, client->adapter, client->addr);776switch (instance) {777case 0:778printk(KERN_ERR "au8522_decoder allocation failed\n");779return -EIO;780case 1:781/* new demod instance */782printk(KERN_INFO "au8522_decoder creating new instance...\n");783break;784default:785/* existing demod instance */786printk(KERN_INFO "au8522_decoder attach existing instance.\n");787break;788}789790demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL);791if (demod_config == NULL) {792if (instance == 1)793kfree(state);794return -ENOMEM;795}796demod_config->demod_address = 0x8e >> 1;797798state->config = demod_config;799state->i2c = client->adapter;800801sd = &state->sd;802v4l2_i2c_subdev_init(sd, client, &au8522_ops);803804state->c = client;805state->vid_input = AU8522_COMPOSITE_CH1;806state->aud_input = AU8522_AUDIO_NONE;807state->id = 8522;808state->rev = 0;809810/* Jam open the i2c gate to the tuner */811au8522_writereg(state, 0x106, 1);812813return 0;814}815816static int au8522_remove(struct i2c_client *client)817{818struct v4l2_subdev *sd = i2c_get_clientdata(client);819v4l2_device_unregister_subdev(sd);820au8522_release_state(to_state(sd));821return 0;822}823824static const struct i2c_device_id au8522_id[] = {825{"au8522", 0},826{}827};828829MODULE_DEVICE_TABLE(i2c, au8522_id);830831static struct i2c_driver au8522_driver = {832.driver = {833.owner = THIS_MODULE,834.name = "au8522",835},836.probe = au8522_probe,837.remove = au8522_remove,838.id_table = au8522_id,839};840841static __init int init_au8522(void)842{843return i2c_add_driver(&au8522_driver);844}845846static __exit void exit_au8522(void)847{848i2c_del_driver(&au8522_driver);849}850851module_init(init_au8522);852module_exit(exit_au8522);853854855