msp34xx.c revision 123291
1179407Sobrien/*- 2179407Sobrien * Copyright (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org> 3218822Sdim * All rights reserved. 4179407Sobrien * 5179407Sobrien * Redistribution and use in source and binary forms, with or without 6179407Sobrien * modification, are permitted provided that the following conditions 7179407Sobrien * are met: 8179407Sobrien * 1. Redistributions of source code must retain the above copyright 9179407Sobrien * notice, this list of conditions and the following disclaimer. 10179407Sobrien * 2. Redistributions in binary form must reproduce the above copyright 11179407Sobrien * notice, this list of conditions and the following disclaimer in the 12179407Sobrien * documentation and/or other materials provided with the distribution. 13179407Sobrien * 14179407Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15179407Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16179407Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17179407Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18179407Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19179407Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20179407Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21179407Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22179407Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23179407Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24179407Sobrien * SUCH DAMAGE. 25179407Sobrien * 26218822Sdim * $FreeBSD: head/sys/dev/bktr/msp34xx.c 123291 2003-12-08 07:59:18Z obrien $ 27179407Sobrien */ 28179407Sobrien 29179407Sobrien/* 30179407Sobrien * programming the msp34* sound processor family 31179407Sobrien * 32218822Sdim * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org> 33179407Sobrien * 34179407Sobrien * what works and what doesn't: 35179407Sobrien * 36179407Sobrien * AM-Mono 37179407Sobrien * Support for Hauppauge cards added (decoding handled by tuner) added by 38179407Sobrien * Frederic Crozat <fcrozat@mail.dotcom.fr> 39179407Sobrien * 40218822Sdim * FM-Mono 41179407Sobrien * should work. The stereo modes are backward compatible to FM-mono, 42179407Sobrien * therefore FM-Mono should be allways available. 43179407Sobrien * 44179407Sobrien * FM-Stereo (B/G, used in germany) 45179407Sobrien * should work, with autodetect 46179407Sobrien * 47179407Sobrien * FM-Stereo (satellite) 48179407Sobrien * should work, no autodetect (i.e. default is mono, but you can 49179407Sobrien * switch to stereo -- untested) 50179407Sobrien * 51179407Sobrien * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) 52179407Sobrien * should work, with autodetect. Support for NICAM was added by 53179407Sobrien * Pekka Pietikainen <pp@netppl.fi> 54179407Sobrien * 55179407Sobrien * 56179407Sobrien * TODO: 57179407Sobrien * - better SAT support 58179407Sobrien * 59179407Sobrien * 60179407Sobrien * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch) 61179407Sobrien * using soundcore instead of OSS 62179407Sobrien * 63179407Sobrien * 64179407Sobrien * The FreeBSD modifications by Alexander Langer <alex@FreeBSD.org> 65179407Sobrien * are in the public domain. Please contact me (Alex) and not Gerd for 66179407Sobrien * any problems you encounter under FreeBSD. 67179407Sobrien * 68179407Sobrien * FreeBSD TODO: 69179407Sobrien * - mutex handling (currently not mp-safe) 70179407Sobrien * - the various options here as loader tunables or compile time or whatever 71179407Sobrien * - how does the new dolby flag work with the current dpl_* stuff? 72179407Sobrien * Maybe it's just enough to set the dolby flag to 1 and it works. 73179407Sobrien * As I don't have a dolby card myself, I can't test it, though. 74179407Sobrien */ 75179407Sobrien 76179407Sobrien#include "opt_bktr.h" /* Include any kernel config options */ 77179407Sobrien#ifdef BKTR_NEW_MSP34XX_DRIVER /* file only needed for new driver */ 78179407Sobrien 79179407Sobrien#include <sys/param.h> 80179407Sobrien#include <sys/systm.h> 81179407Sobrien#include <sys/kernel.h> 82179407Sobrien#include <sys/vnode.h> 83179407Sobrien 84179407Sobrien#include <sys/unistd.h> 85179407Sobrien#include <sys/kthread.h> 86179407Sobrien#include <sys/malloc.h> 87179407Sobrien 88179407Sobrien#include <machine/bus.h> /* required by bktr_reg.h */ 89179407Sobrien 90179407Sobrien#include <dev/btkr/ioctl_meteor.h> 91179407Sobrien#include <dev/btkr/ioctl_bt848.h> /* extensions to ioctl_meteor.h */ 92179407Sobrien#include <dev/bktr/bktr_reg.h> 93179407Sobrien#include <dev/bktr/bktr_tuner.h> 94179407Sobrien#include <dev/bktr/bktr_audio.h> 95179407Sobrien#include <dev/bktr/bktr_core.h> 96179407Sobrien 97179407Sobrien#define VIDEO_MODE_PAL 0 98179407Sobrien#define VIDEO_MODE_NTSC 1 99179407Sobrien#define VIDEO_MODE_SECAM 2 100179407Sobrien#define VIDEO_MODE_AUTO 3 101179407Sobrien 102179407Sobrien#define VIDEO_SOUND_MONO 1 103179407Sobrien#define VIDEO_SOUND_STEREO 2 104179407Sobrien#define VIDEO_SOUND_LANG1 4 105179407Sobrien#define VIDEO_SOUND_LANG2 8 106179407Sobrien 107179407Sobrien#define DFP_COUNT 0x41 108179407Sobrienstatic const int bl_dfp[] = { 109179407Sobrien 0x00, 0x01, 0x02, 0x03, 0x06, 0x08, 0x09, 0x0a, 110179407Sobrien 0x0b, 0x0d, 0x0e, 0x10 111179407Sobrien}; 112179407Sobrien 113179407Sobrienstruct msp3400c { 114179407Sobrien int simple; 115179407Sobrien int nicam; 116179407Sobrien int mode; 117179407Sobrien int norm; 118179407Sobrien int stereo; 119179407Sobrien int nicam_on; 120179407Sobrien int acb; 121179407Sobrien int main, second; /* sound carrier */ 122179407Sobrien int input; 123179407Sobrien 124179407Sobrien int muted; 125179407Sobrien int left, right; /* volume */ 126179407Sobrien int bass, treble; 127179407Sobrien 128179407Sobrien /* shadow register set */ 129179407Sobrien int dfp_regs[DFP_COUNT]; 130179407Sobrien 131179407Sobrien /* thread */ 132179407Sobrien struct proc *kthread; 133179407Sobrien char *threaddesc; 134179407Sobrien 135179407Sobrien int active,restart,rmmod; 136179407Sobrien 137179407Sobrien int watch_stereo; 138179407Sobrien int halt_thread; 139179407Sobrien}; 140179407Sobrien 141179407Sobrien#define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ 142179407Sobrien 143179407Sobrien/* ---------------------------------------------------------------------- */ 144179407Sobrien 145179407Sobrien#define dprintk(...) do { \ 146179407Sobrien if (bootverbose) { \ 147179407Sobrien printf("%s: ", bktr_name(client)); \ 148179407Sobrien printf(__VA_ARGS__); \ 149179407Sobrien } \ 150179407Sobrien} while (0) 151179407Sobrien 152179407Sobrien/* ---------------------------------------------------------------------- */ 153179407Sobrien 154179407Sobrien#define I2C_MSP3400C 0x80 155179407Sobrien#define I2C_MSP3400C_DEM 0x10 156179407Sobrien#define I2C_MSP3400C_DFP 0x12 157179407Sobrien 158179407Sobrien/* ----------------------------------------------------------------------- */ 159179407Sobrien/* functions for talking to the MSP3400C Sound processor */ 160179407Sobrien 161179407Sobrienstatic int msp3400c_reset(bktr_ptr_t client) 162179407Sobrien{ 163179407Sobrien /* use our own which handles config(8) options */ 164179407Sobrien msp_dpl_reset(client, client->msp_addr); 165179407Sobrien 166179407Sobrien return 0; 167179407Sobrien} 168179407Sobrien 169179407Sobrienstatic int 170179407Sobrienmsp3400c_read(bktr_ptr_t client, int dev, int addr) 171179407Sobrien{ 172179407Sobrien /* use our own */ 173179407Sobrien return(msp_dpl_read(client, client->msp_addr, dev, addr)); 174179407Sobrien} 175179407Sobrien 176179407Sobrienstatic int 177179407Sobrienmsp3400c_write(bktr_ptr_t client, int dev, int addr, int val) 178179407Sobrien{ 179179407Sobrien /* use our own */ 180179407Sobrien msp_dpl_write(client, client->msp_addr, dev, addr, val); 181179407Sobrien 182179407Sobrien return(0); 183179407Sobrien} 184179407Sobrien 185179407Sobrien/* ------------------------------------------------------------------------ */ 186179407Sobrien 187179407Sobrien/* This macro is allowed for *constants* only, gcc must calculate it 188179407Sobrien at compile time. Remember -- no floats in kernel mode */ 189179407Sobrien#define MSP_CARRIER(freq) ((int)((float)(freq/18.432)*(1<<24))) 190179407Sobrien 191179407Sobrien#define MSP_MODE_AM_DETECT 0 192179407Sobrien#define MSP_MODE_FM_RADIO 2 193179407Sobrien#define MSP_MODE_FM_TERRA 3 194179407Sobrien#define MSP_MODE_FM_SAT 4 195179407Sobrien#define MSP_MODE_FM_NICAM1 5 196179407Sobrien#define MSP_MODE_FM_NICAM2 6 197179407Sobrien#define MSP_MODE_AM_NICAM 7 198179407Sobrien#define MSP_MODE_BTSC 8 199179407Sobrien#define MSP_MODE_EXTERN 9 200179407Sobrien 201179407Sobrienstatic struct MSP_INIT_DATA_DEM { 202179407Sobrien int fir1[6]; 203179407Sobrien int fir2[6]; 204179407Sobrien int cdo1; 205179407Sobrien int cdo2; 206179407Sobrien int ad_cv; 207179407Sobrien int mode_reg; 208179407Sobrien int dfp_src; 209179407Sobrien int dfp_matrix; 210179407Sobrien} msp_init_data[] = { 211179407Sobrien /* AM (for carrier detect / msp3400) */ 212179407Sobrien { { 75, 19, 36, 35, 39, 40 }, { 75, 19, 36, 35, 39, 40 }, 213179407Sobrien MSP_CARRIER(5.5), MSP_CARRIER(5.5), 214179407Sobrien 0x00d0, 0x0500, 0x0020, 0x3000}, 215179407Sobrien 216179407Sobrien /* AM (for carrier detect / msp3410) */ 217179407Sobrien { { -1, -1, -8, 2, 59, 126 }, { -1, -1, -8, 2, 59, 126 }, 218179407Sobrien MSP_CARRIER(5.5), MSP_CARRIER(5.5), 219179407Sobrien 0x00d0, 0x0100, 0x0020, 0x3000}, 220179407Sobrien 221179407Sobrien /* FM Radio */ 222179407Sobrien { { -8, -8, 4, 6, 78, 107 }, { -8, -8, 4, 6, 78, 107 }, 223179407Sobrien MSP_CARRIER(10.7), MSP_CARRIER(10.7), 224179407Sobrien 0x00d0, 0x0480, 0x0020, 0x3000 }, 225179407Sobrien 226179407Sobrien /* Terrestial FM-mono + FM-stereo */ 227179407Sobrien { { 3, 18, 27, 48, 66, 72 }, { 3, 18, 27, 48, 66, 72 }, 228179407Sobrien MSP_CARRIER(5.5), MSP_CARRIER(5.5), 229179407Sobrien 0x00d0, 0x0480, 0x0030, 0x3000}, 230179407Sobrien 231179407Sobrien /* Sat FM-mono */ 232179407Sobrien { { 1, 9, 14, 24, 33, 37 }, { 3, 18, 27, 48, 66, 72 }, 233179407Sobrien MSP_CARRIER(6.5), MSP_CARRIER(6.5), 234179407Sobrien 0x00c6, 0x0480, 0x0000, 0x3000}, 235179407Sobrien 236179407Sobrien /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ 237179407Sobrien { { -2, -8, -10, 10, 50, 86 }, { 3, 18, 27, 48, 66, 72 }, 238179407Sobrien MSP_CARRIER(5.5), MSP_CARRIER(5.5), 239179407Sobrien 0x00d0, 0x0040, 0x0120, 0x3000}, 240179407Sobrien 241179407Sobrien /* NICAM/FM -- I (6.0/6.552) */ 242179407Sobrien { { 2, 4, -6, -4, 40, 94 }, { 3, 18, 27, 48, 66, 72 }, 243179407Sobrien MSP_CARRIER(6.0), MSP_CARRIER(6.0), 244179407Sobrien 0x00d0, 0x0040, 0x0120, 0x3000}, 245179407Sobrien 246179407Sobrien /* NICAM/AM -- L (6.5/5.85) */ 247179407Sobrien { { -2, -8, -10, 10, 50, 86 }, { -4, -12, -9, 23, 79, 126 }, 248179407Sobrien MSP_CARRIER(6.5), MSP_CARRIER(6.5), 249179407Sobrien 0x00c6, 0x0140, 0x0120, 0x7c03}, 250179407Sobrien}; 251179407Sobrien 252179407Sobrienstruct CARRIER_DETECT { 253179407Sobrien int cdo; 254179407Sobrien char *name; 255179407Sobrien}; 256179407Sobrien 257179407Sobrienstatic struct CARRIER_DETECT carrier_detect_main[] = { 258179407Sobrien /* main carrier */ 259218822Sdim { MSP_CARRIER(4.5), "4.5 NTSC" }, 260218822Sdim { MSP_CARRIER(5.5), "5.5 PAL B/G" }, 261218822Sdim { MSP_CARRIER(6.0), "6.0 PAL I" }, 262179407Sobrien { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } 263218822Sdim}; 264179407Sobrien 265179407Sobrienstatic struct CARRIER_DETECT carrier_detect_55[] = { 266179407Sobrien /* PAL B/G */ 267179407Sobrien { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, 268179407Sobrien { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } 269179407Sobrien}; 270179407Sobrien 271179407Sobrienstatic struct CARRIER_DETECT carrier_detect_65[] = { 272179407Sobrien /* PAL SAT / SECAM */ 273179407Sobrien { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, 274179407Sobrien { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, 275179407Sobrien { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, 276179407Sobrien { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, 277179407Sobrien { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, 278179407Sobrien { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, 279179407Sobrien}; 280179407Sobrien 281179407Sobrien#define CARRIER_COUNT(x) (sizeof(x)/sizeof(struct CARRIER_DETECT)) 282179407Sobrien 283179407Sobrien/* ----------------------------------------------------------------------- */ 284179407Sobrien 285179407Sobrien#define SCART_MASK 0 286179407Sobrien#define SCART_IN1 1 287179407Sobrien#define SCART_IN2 2 288179407Sobrien#define SCART_IN1_DA 3 289179407Sobrien#define SCART_IN2_DA 4 290179407Sobrien#define SCART_IN3 5 291179407Sobrien#define SCART_IN4 6 292179407Sobrien#define SCART_MONO 7 293179407Sobrien#define SCART_MUTE 8 294179407Sobrien 295179407Sobrienstatic int scarts[3][9] = { 296179407Sobrien /* MASK IN1 IN2 IN1_DA IN2_DA IN3 IN4 MONO MUTE */ 297179407Sobrien { 0x0320, 0x0000, 0x0200, -1, -1, 0x0300, 0x0020, 0x0100, 0x0320 }, 298179407Sobrien { 0x0c40, 0x0440, 0x0400, 0x0c00, 0x0040, 0x0000, 0x0840, 0x0800, 0x0c40 }, 299179407Sobrien { 0x3080, 0x1000, 0x1080, 0x0000, 0x0080, 0x2080, 0x3080, 0x2000, 0x3000 }, 300179407Sobrien}; 301179407Sobrien 302179407Sobrienstatic char *scart_names[] = { 303179407Sobrien "mask", "in1", "in2", "in1 da", "in2 da", "in3", "in4", "mono", "mute" 304179407Sobrien}; 305179407Sobrien 306179407Sobrienstatic void 307179407Sobrienmsp3400c_set_scart(bktr_ptr_t client, int in, int out) 308179407Sobrien{ 309179407Sobrien struct msp3400c *msp = client->msp3400c_info; 310179407Sobrien 311179407Sobrien if (-1 == scarts[out][in]) 312179407Sobrien return; 313179407Sobrien 314179407Sobrien dprintk("msp34xx: scart switch: %s => %d\n",scart_names[in],out); 315179407Sobrien msp->acb &= ~scarts[out][SCART_MASK]; 316179407Sobrien msp->acb |= scarts[out][in]; 317179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0013, msp->acb); 318179407Sobrien} 319179407Sobrien 320179407Sobrien/* ------------------------------------------------------------------------ */ 321179407Sobrien 322179407Sobrienstatic void msp3400c_setcarrier(bktr_ptr_t client, int cdo1, int cdo2) 323179407Sobrien{ 324179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x0093, cdo1 & 0xfff); 325179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12); 326179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff); 327179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12); 328179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ 329179407Sobrien} 330179407Sobrien 331179407Sobrienstatic void msp3400c_setvolume(bktr_ptr_t client, 332179407Sobrien int muted, int left, int right) 333179407Sobrien{ 334179407Sobrien int vol = 0,val = 0,balance = 0; 335179407Sobrien 336179407Sobrien if (!muted) { 337179407Sobrien vol = (left > right) ? left : right; 338179407Sobrien val = (vol * 0x73 / 65535) << 8; 339179407Sobrien } 340179407Sobrien if (vol > 0) { 341179407Sobrien balance = ((right-left) * 127) / vol; 342179407Sobrien } 343179407Sobrien 344179407Sobrien dprintk("msp34xx: setvolume: mute=%s %d:%d v=0x%02x b=0x%02x\n", 345179407Sobrien muted ? "on" : "off", left, right, val>>8, balance); 346179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ 347179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ 348179407Sobrien /* scart - on/off only */ 349179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); 350179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0001, balance << 8); 351179407Sobrien} 352179407Sobrien 353179407Sobrienstatic void msp3400c_setbass(bktr_ptr_t client, int bass) 354179407Sobrien{ 355179407Sobrien int val = ((bass-32768) * 0x60 / 65535) << 8; 356179407Sobrien 357179407Sobrien dprintk("msp34xx: setbass: %d 0x%02x\n",bass, val>>8); 358179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */ 359179407Sobrien} 360179407Sobrien 361179407Sobrienstatic void msp3400c_settreble(bktr_ptr_t client, int treble) 362179407Sobrien{ 363179407Sobrien int val = ((treble-32768) * 0x60 / 65535) << 8; 364179407Sobrien 365179407Sobrien dprintk("msp34xx: settreble: %d 0x%02x\n",treble, val>>8); 366179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */ 367179407Sobrien} 368179407Sobrien 369179407Sobrienstatic void msp3400c_setmode(bktr_ptr_t client, int type) 370179407Sobrien{ 371179407Sobrien struct msp3400c *msp = client->msp3400c_info; 372179407Sobrien int i; 373179407Sobrien 374179407Sobrien dprintk("msp3400: setmode: %d\n",type); 375179407Sobrien msp->mode = type; 376179407Sobrien msp->stereo = VIDEO_SOUND_MONO; 377179407Sobrien 378179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ 379179407Sobrien msp_init_data[type].ad_cv); 380179407Sobrien 381179407Sobrien for (i = 5; i >= 0; i--) /* fir 1 */ 382179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x0001, 383179407Sobrien msp_init_data[type].fir1[i]); 384179407Sobrien 385179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */ 386179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0040); 387179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0000); 388179407Sobrien for (i = 5; i >= 0; i--) 389179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 390179407Sobrien msp_init_data[type].fir2[i]); 391179407Sobrien 392179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */ 393179407Sobrien msp_init_data[type].mode_reg); 394179407Sobrien 395179407Sobrien msp3400c_setcarrier(client, msp_init_data[type].cdo1, 396179407Sobrien msp_init_data[type].cdo2); 397179407Sobrien 398179407Sobrien msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ 399179407Sobrien 400179407Sobrien if (client->dolby) { 401179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, 402179407Sobrien 0x0520); /* I2S1 */ 403179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, 404179407Sobrien 0x0620); /* I2S2 */ 405179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, 406179407Sobrien msp_init_data[type].dfp_src); 407179407Sobrien } else { 408179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, 409179407Sobrien msp_init_data[type].dfp_src); 410179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, 411179407Sobrien msp_init_data[type].dfp_src); 412179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, 413179407Sobrien msp_init_data[type].dfp_src); 414179407Sobrien } 415179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a, 416179407Sobrien msp_init_data[type].dfp_src); 417179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 418179407Sobrien msp_init_data[type].dfp_matrix); 419179407Sobrien 420179407Sobrien if (msp->nicam) { 421179407Sobrien /* nicam prescale */ 422179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x5a00); /* was: 0x3000 */ 423179407Sobrien } 424179407Sobrien} 425179407Sobrien 426179407Sobrien/* turn on/off nicam + stereo */ 427179407Sobrienstatic void msp3400c_setstereo(bktr_ptr_t client, int mode) 428179407Sobrien{ 429179407Sobrien static char *strmode[] = { "0", "mono", "stereo", "3", 430179407Sobrien "lang1", "5", "6", "7", "lang2" }; 431179407Sobrien struct msp3400c *msp = client->msp3400c_info; 432179407Sobrien int nicam=0; /* channel source: FM/AM or nicam */ 433179407Sobrien int src=0; 434179407Sobrien 435179407Sobrien /* switch demodulator */ 436179407Sobrien switch (msp->mode) { 437179407Sobrien case MSP_MODE_FM_TERRA: 438179407Sobrien dprintk("msp3400: FM setstereo: %s\n",strmode[mode]); 439179407Sobrien msp3400c_setcarrier(client,msp->second,msp->main); 440179407Sobrien switch (mode) { 441179407Sobrien case VIDEO_SOUND_STEREO: 442179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3001); 443179407Sobrien break; 444179407Sobrien case VIDEO_SOUND_MONO: 445179407Sobrien case VIDEO_SOUND_LANG1: 446179407Sobrien case VIDEO_SOUND_LANG2: 447179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3000); 448179407Sobrien break; 449179407Sobrien } 450179407Sobrien break; 451179407Sobrien case MSP_MODE_FM_SAT: 452179407Sobrien dprintk("msp3400: SAT setstereo: %s\n",strmode[mode]); 453179407Sobrien switch (mode) { 454179407Sobrien case VIDEO_SOUND_MONO: 455179407Sobrien msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); 456179407Sobrien break; 457179407Sobrien case VIDEO_SOUND_STEREO: 458179407Sobrien msp3400c_setcarrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); 459179407Sobrien break; 460179407Sobrien case VIDEO_SOUND_LANG1: 461179407Sobrien msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); 462179407Sobrien break; 463179407Sobrien case VIDEO_SOUND_LANG2: 464179407Sobrien msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); 465179407Sobrien break; 466179407Sobrien } 467179407Sobrien break; 468179407Sobrien case MSP_MODE_FM_NICAM1: 469179407Sobrien case MSP_MODE_FM_NICAM2: 470179407Sobrien case MSP_MODE_AM_NICAM: 471179407Sobrien dprintk("msp3400: NICAM setstereo: %s\n",strmode[mode]); 472179407Sobrien msp3400c_setcarrier(client,msp->second,msp->main); 473179407Sobrien if (msp->nicam_on) 474179407Sobrien nicam=0x0100; 475179407Sobrien break; 476179407Sobrien case MSP_MODE_BTSC: 477179407Sobrien dprintk("msp3400: BTSC setstereo: %s\n",strmode[mode]); 478179407Sobrien nicam=0x0300; 479179407Sobrien break; 480179407Sobrien case MSP_MODE_EXTERN: 481179407Sobrien dprintk("msp3400: extern setstereo: %s\n",strmode[mode]); 482179407Sobrien nicam = 0x0200; 483179407Sobrien break; 484179407Sobrien case MSP_MODE_FM_RADIO: 485179407Sobrien dprintk("msp3400: FM-Radio setstereo: %s\n",strmode[mode]); 486179407Sobrien break; 487179407Sobrien default: 488179407Sobrien dprintk("msp3400: mono setstereo\n"); 489179407Sobrien return; 490179407Sobrien } 491179407Sobrien 492179407Sobrien /* switch audio */ 493179407Sobrien switch (mode) { 494179407Sobrien case VIDEO_SOUND_STEREO: 495179407Sobrien src = 0x0020 | nicam; 496179407Sobrien#if 0 497179407Sobrien /* spatial effect */ 498179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0005,0x4000); 499179407Sobrien#endif 500179407Sobrien break; 501179407Sobrien case VIDEO_SOUND_MONO: 502179407Sobrien if (msp->mode == MSP_MODE_AM_NICAM) { 503179407Sobrien dprintk("msp3400: switching to AM mono\n"); 504179407Sobrien /* AM mono decoding is handled by tuner, not MSP chip */ 505179407Sobrien /* SCART switching control register */ 506179407Sobrien msp3400c_set_scart(client,SCART_MONO,0); 507179407Sobrien src = 0x0200; 508179407Sobrien break; 509179407Sobrien } 510179407Sobrien case VIDEO_SOUND_LANG1: 511179407Sobrien src = 0x0000 | nicam; 512179407Sobrien break; 513179407Sobrien case VIDEO_SOUND_LANG2: 514179407Sobrien src = 0x0010 | nicam; 515179407Sobrien break; 516179407Sobrien } 517179407Sobrien dprintk("msp3400: setstereo final source/matrix = 0x%x\n", src); 518179407Sobrien 519179407Sobrien if (client->dolby) { 520179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,0x0520); 521179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,0x0620); 522179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); 523179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); 524179407Sobrien } else { 525179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,src); 526179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,src); 527179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); 528179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); 529179407Sobrien } 530179407Sobrien} 531179407Sobrien 532179407Sobrienstatic void 533179407Sobrienmsp3400c_print_mode(struct msp3400c *msp) 534179407Sobrien{ 535179407Sobrien if (msp->main == msp->second) { 536179407Sobrien printf("bktr: msp3400: mono sound carrier: %d.%03d MHz\n", 537179407Sobrien msp->main/910000,(msp->main/910)%1000); 538179407Sobrien } else { 539179407Sobrien printf("bktr: msp3400: main sound carrier: %d.%03d MHz\n", 540179407Sobrien msp->main/910000,(msp->main/910)%1000); 541179407Sobrien } 542179407Sobrien if (msp->mode == MSP_MODE_FM_NICAM1 || 543179407Sobrien msp->mode == MSP_MODE_FM_NICAM2) 544179407Sobrien printf("bktr: msp3400: NICAM/FM carrier : %d.%03d MHz\n", 545179407Sobrien msp->second/910000,(msp->second/910)%1000); 546179407Sobrien if (msp->mode == MSP_MODE_AM_NICAM) 547179407Sobrien printf("bktr: msp3400: NICAM/AM carrier : %d.%03d MHz\n", 548179407Sobrien msp->second/910000,(msp->second/910)%1000); 549218822Sdim if (msp->mode == MSP_MODE_FM_TERRA && 550218822Sdim msp->main != msp->second) { 551218822Sdim printf("bktr: msp3400: FM-stereo carrier : %d.%03d MHz\n", 552218822Sdim msp->second/910000,(msp->second/910)%1000); 553218822Sdim } 554218822Sdim} 555218822Sdim 556218822Sdimstatic void 557218822Sdimmsp3400c_restore_dfp(bktr_ptr_t client) 558218822Sdim{ 559218822Sdim struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 560218822Sdim int i; 561218822Sdim 562218822Sdim for (i = 0; i < DFP_COUNT; i++) { 563218822Sdim if (-1 == msp->dfp_regs[i]) 564218822Sdim continue; 565218822Sdim msp3400c_write(client,I2C_MSP3400C_DFP, i, msp->dfp_regs[i]); 566218822Sdim } 567218822Sdim} 568218822Sdim 569218822Sdim/* ----------------------------------------------------------------------- */ 570218822Sdim 571218822Sdimstruct REGISTER_DUMP { 572218822Sdim int addr; 573218822Sdim char *name; 574218822Sdim}; 575218822Sdim 576218822Sdimstruct REGISTER_DUMP d1[] = { 577218822Sdim { 0x007e, "autodetect" }, 578218822Sdim { 0x0023, "C_AD_BITS " }, 579218822Sdim { 0x0038, "ADD_BITS " }, 580218822Sdim { 0x003e, "CIB_BITS " }, 581218822Sdim { 0x0057, "ERROR_RATE" }, 582218822Sdim}; 583218822Sdim 584218822Sdimstatic int 585218822Sdimautodetect_stereo(bktr_ptr_t client) 586218822Sdim{ 587218822Sdim struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 588218822Sdim int val; 589218822Sdim int newstereo = msp->stereo; 590218822Sdim int newnicam = msp->nicam_on; 591218822Sdim int update = 0; 592218822Sdim 593218822Sdim switch (msp->mode) { 594218822Sdim case MSP_MODE_FM_TERRA: 595218822Sdim val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18); 596218822Sdim if (val > 32767) 597218822Sdim val -= 65536; 598218822Sdim dprintk("msp34xx: stereo detect register: %d\n",val); 599218822Sdim 600218822Sdim if (val > 4096) { 601218822Sdim newstereo = VIDEO_SOUND_STEREO | VIDEO_SOUND_MONO; 602218822Sdim } else if (val < -4096) { 603218822Sdim newstereo = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; 604218822Sdim } else { 605218822Sdim newstereo = VIDEO_SOUND_MONO; 606218822Sdim } 607218822Sdim newnicam = 0; 608218822Sdim break; 609218822Sdim case MSP_MODE_FM_NICAM1: 610218822Sdim case MSP_MODE_FM_NICAM2: 611218822Sdim case MSP_MODE_AM_NICAM: 612218822Sdim val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x23); 613218822Sdim dprintk("msp34xx: nicam sync=%d, mode=%d\n",val & 1, (val & 0x1e) >> 1); 614218822Sdim 615218822Sdim if (val & 1) { 616218822Sdim /* nicam synced */ 617218822Sdim switch ((val & 0x1e) >> 1) { 618218822Sdim case 0: 619218822Sdim case 8: 620218822Sdim newstereo = VIDEO_SOUND_STEREO; 621218822Sdim break; 622218822Sdim case 1: 623218822Sdim case 9: 624218822Sdim newstereo = VIDEO_SOUND_MONO 625218822Sdim | VIDEO_SOUND_LANG1; 626218822Sdim break; 627218822Sdim case 2: 628218822Sdim case 10: 629218822Sdim newstereo = VIDEO_SOUND_MONO 630218822Sdim | VIDEO_SOUND_LANG1 631218822Sdim | VIDEO_SOUND_LANG2; 632218822Sdim break; 633218822Sdim default: 634218822Sdim newstereo = VIDEO_SOUND_MONO; 635218822Sdim break; 636218822Sdim } 637218822Sdim newnicam=1; 638218822Sdim } else { 639218822Sdim newnicam = 0; 640218822Sdim newstereo = VIDEO_SOUND_MONO; 641218822Sdim } 642218822Sdim break; 643218822Sdim case MSP_MODE_BTSC: 644218822Sdim val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200); 645218822Sdim dprintk("msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n", 646218822Sdim val, 647218822Sdim (val & 0x0002) ? "no" : "yes", 648218822Sdim (val & 0x0004) ? "no" : "yes", 649218822Sdim (val & 0x0040) ? "stereo" : "mono", 650218822Sdim (val & 0x0080) ? ", nicam 2nd mono" : "", 651218822Sdim (val & 0x0100) ? ", bilingual/SAP" : ""); 652218822Sdim newstereo = VIDEO_SOUND_MONO; 653218822Sdim if (val & 0x0040) newstereo |= VIDEO_SOUND_STEREO; 654218822Sdim if (val & 0x0100) newstereo |= VIDEO_SOUND_LANG1; 655218822Sdim break; 656218822Sdim } 657218822Sdim if (newstereo != msp->stereo) { 658218822Sdim update = 1; 659218822Sdim dprintk("msp34xx: watch: stereo %d => %d\n", 660218822Sdim msp->stereo,newstereo); 661218822Sdim msp->stereo = newstereo; 662218822Sdim } 663218822Sdim if (newnicam != msp->nicam_on) { 664218822Sdim update = 1; 665218822Sdim dprintk("msp34xx: watch: nicam %d => %d\n", 666218822Sdim msp->nicam_on,newnicam); 667218822Sdim msp->nicam_on = newnicam; 668218822Sdim } 669218822Sdim return update; 670218822Sdim} 671218822Sdim 672218822Sdim/* 673218822Sdim * A kernel thread for msp3400 control -- we don't want to block the 674218822Sdim * in the ioctl while doing the sound carrier & stereo detect 675218822Sdim */ 676218822Sdim 677218822Sdim/* stereo/multilang monitoring */ 678218822Sdimstatic void watch_stereo(bktr_ptr_t client) 679218822Sdim{ 680218822Sdim struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 681218822Sdim 682218822Sdim if (autodetect_stereo(client)) { 683218822Sdim if (msp->stereo & VIDEO_SOUND_STEREO) 684218822Sdim msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 685218822Sdim else if (msp->stereo & VIDEO_SOUND_LANG1) 686218822Sdim msp3400c_setstereo(client,VIDEO_SOUND_LANG1); 687218822Sdim else 688218822Sdim msp3400c_setstereo(client,VIDEO_SOUND_MONO); 689218822Sdim } 690218822Sdim if (client->stereo_once) 691218822Sdim msp->watch_stereo = 0; 692218822Sdim 693218822Sdim} 694218822Sdim 695218822Sdimstatic void msp3400c_thread(void *data) 696218822Sdim{ 697218822Sdim bktr_ptr_t client = data; 698218822Sdim struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 699218822Sdim 700218822Sdim struct CARRIER_DETECT *cd; 701218822Sdim int count, max1,max2,val1,val2, val,this; 702218822Sdim 703218822Sdim dprintk("msp3400: thread started\n"); 704218822Sdim 705218822Sdim for (;;) { 706218822Sdim if (msp->rmmod) 707218822Sdim goto done; 708218822Sdim tsleep(msp->kthread, PRIBIO, "idle", 0); 709218822Sdim if (msp->rmmod) 710218822Sdim goto done; 711218822Sdim if (msp->halt_thread) { 712218822Sdim msp->watch_stereo = 0; 713218822Sdim msp->halt_thread = 0; 714218822Sdim continue; 715218822Sdim } 716218822Sdim 717218822Sdim if (VIDEO_MODE_RADIO == msp->norm || 718179407Sobrien MSP_MODE_EXTERN == msp->mode) 719179407Sobrien continue; /* nothing to do */ 720179407Sobrien 721179407Sobrien msp->active = 1; 722179407Sobrien 723179407Sobrien if (msp->watch_stereo) { 724179407Sobrien watch_stereo(client); 725179407Sobrien msp->active = 0; 726179407Sobrien continue; 727179407Sobrien } 728179407Sobrien 729179407Sobrien /* some time for the tuner to sync */ 730179407Sobrien tsleep(msp->kthread, PRIBIO, "tuner sync", hz/2); 731179407Sobrien 732179407Sobrien restart: 733179407Sobrien if (VIDEO_MODE_RADIO == msp->norm || 734179407Sobrien MSP_MODE_EXTERN == msp->mode) 735179407Sobrien continue; /* nothing to do */ 736179407Sobrien msp->restart = 0; 737218822Sdim msp3400c_setvolume(client, msp->muted, 0, 0); 738218822Sdim msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); 739218822Sdim val1 = val2 = 0; 740179407Sobrien max1 = max2 = -1; 741179407Sobrien msp->watch_stereo = 0; 742179407Sobrien 743179407Sobrien /* carrier detect pass #1 -- main carrier */ 744179407Sobrien cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main); 745179407Sobrien 746179407Sobrien if (client->amsound && (msp->norm == VIDEO_MODE_SECAM)) { 747179407Sobrien /* autodetect doesn't work well with AM ... */ 748179407Sobrien max1 = 3; 749179407Sobrien count = 0; 750218822Sdim dprintk("msp3400: AM sound override\n"); 751179407Sobrien } 752179407Sobrien 753179407Sobrien for (this = 0; this < count; this++) { 754179407Sobrien msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); 755218822Sdim 756179407Sobrien tsleep(msp->kthread, PRIBIO, "carrier detect", hz/100); 757218822Sdim 758179407Sobrien if (msp->restart) 759179407Sobrien msp->restart = 0; 760179407Sobrien 761179407Sobrien val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); 762179407Sobrien if (val > 32767) 763179407Sobrien val -= 65536; 764179407Sobrien if (val1 < val) 765179407Sobrien val1 = val, max1 = this; 766179407Sobrien dprintk("msp3400: carrier1 val: %5d / %s\n", val,cd[this].name); 767179407Sobrien } 768218822Sdim 769218822Sdim /* carrier detect pass #2 -- second (stereo) carrier */ 770218822Sdim switch (max1) { 771179407Sobrien case 1: /* 5.5 */ 772218822Sdim cd = carrier_detect_55; count = CARRIER_COUNT(carrier_detect_55); 773218822Sdim break; 774218822Sdim case 3: /* 6.5 */ 775218822Sdim cd = carrier_detect_65; count = CARRIER_COUNT(carrier_detect_65); 776218822Sdim break; 777218822Sdim case 0: /* 4.5 */ 778218822Sdim case 2: /* 6.0 */ 779218822Sdim default: 780179407Sobrien cd = NULL; count = 0; 781179407Sobrien break; 782179407Sobrien } 783218822Sdim 784179407Sobrien if (client->amsound && (msp->norm == VIDEO_MODE_SECAM)) { 785179407Sobrien /* autodetect doesn't work well with AM ... */ 786179407Sobrien cd = NULL; count = 0; max2 = 0; 787218822Sdim } 788179407Sobrien for (this = 0; this < count; this++) { 789218822Sdim msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); 790218822Sdim 791218822Sdim tsleep(msp->kthread, PRIBIO, "carrier detection", hz/100); 792179407Sobrien if (msp->restart) 793218822Sdim goto restart; 794218822Sdim 795179407Sobrien val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); 796179407Sobrien if (val > 32767) 797179407Sobrien val -= 65536; 798218822Sdim if (val2 < val) 799179407Sobrien val2 = val, max2 = this; 800179407Sobrien dprintk("msp3400: carrier2 val: %5d / %s\n", val,cd[this].name); 801179407Sobrien } 802218822Sdim 803179407Sobrien /* programm the msp3400 according to the results */ 804218822Sdim msp->main = carrier_detect_main[max1].cdo; 805218822Sdim switch (max1) { 806218822Sdim case 1: /* 5.5 */ 807218822Sdim if (max2 == 0) { 808179407Sobrien /* B/G FM-stereo */ 809179407Sobrien msp->second = carrier_detect_55[max2].cdo; 810179407Sobrien msp3400c_setmode(client, MSP_MODE_FM_TERRA); 811179407Sobrien msp->nicam_on = 0; 812179407Sobrien /* XXX why mono? this probably can do stereo... - Alex*/ 813179407Sobrien msp3400c_setstereo(client, VIDEO_SOUND_MONO); 814179407Sobrien msp->watch_stereo = 1; 815179407Sobrien } else if (max2 == 1 && msp->nicam) { 816179407Sobrien /* B/G NICAM */ 817179407Sobrien msp->second = carrier_detect_55[max2].cdo; 818179407Sobrien msp3400c_setmode(client, MSP_MODE_FM_NICAM1); 819179407Sobrien msp->nicam_on = 1; 820179407Sobrien msp3400c_setcarrier(client, msp->second, msp->main); 821179407Sobrien msp->watch_stereo = 1; 822179407Sobrien } else { 823179407Sobrien goto no_second; 824179407Sobrien } 825218822Sdim break; 826218822Sdim case 2: /* 6.0 */ 827218822Sdim /* PAL I NICAM */ 828218822Sdim msp->second = MSP_CARRIER(6.552); 829179407Sobrien msp3400c_setmode(client, MSP_MODE_FM_NICAM2); 830179407Sobrien msp->nicam_on = 1; 831179407Sobrien msp3400c_setcarrier(client, msp->second, msp->main); 832179407Sobrien msp->watch_stereo = 1; 833179407Sobrien break; 834179407Sobrien case 3: /* 6.5 */ 835179407Sobrien if (max2 == 1 || max2 == 2) { 836179407Sobrien /* D/K FM-stereo */ 837179407Sobrien msp->second = carrier_detect_65[max2].cdo; 838179407Sobrien msp3400c_setmode(client, MSP_MODE_FM_TERRA); 839179407Sobrien msp->nicam_on = 0; 840179407Sobrien msp3400c_setstereo(client, VIDEO_SOUND_MONO); 841179407Sobrien msp->watch_stereo = 1; 842179407Sobrien } else if (max2 == 0 && 843179407Sobrien msp->norm == VIDEO_MODE_SECAM) { 844179407Sobrien /* L NICAM or AM-mono */ 845179407Sobrien msp->second = carrier_detect_65[max2].cdo; 846179407Sobrien msp3400c_setmode(client, MSP_MODE_AM_NICAM); 847179407Sobrien msp->nicam_on = 0; 848179407Sobrien msp3400c_setstereo(client, VIDEO_SOUND_MONO); 849179407Sobrien msp3400c_setcarrier(client, msp->second, msp->main); 850179407Sobrien /* volume prescale for SCART (AM mono input) */ 851179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x000d, 0x1900); 852179407Sobrien msp->watch_stereo = 1; 853179407Sobrien } else if (max2 == 0 && msp->nicam) { 854179407Sobrien /* D/K NICAM */ 855179407Sobrien msp->second = carrier_detect_65[max2].cdo; 856179407Sobrien msp3400c_setmode(client, MSP_MODE_FM_NICAM1); 857179407Sobrien msp->nicam_on = 1; 858179407Sobrien msp3400c_setcarrier(client, msp->second, msp->main); 859179407Sobrien msp->watch_stereo = 1; 860179407Sobrien } else { 861179407Sobrien goto no_second; 862179407Sobrien } 863179407Sobrien break; 864179407Sobrien case 0: /* 4.5 */ 865179407Sobrien default: 866179407Sobrien no_second: 867179407Sobrien msp->second = carrier_detect_main[max1].cdo; 868179407Sobrien msp3400c_setmode(client, MSP_MODE_FM_TERRA); 869179407Sobrien msp->nicam_on = 0; 870179407Sobrien msp3400c_setcarrier(client, msp->second, msp->main); 871179407Sobrien msp->stereo = VIDEO_SOUND_MONO; 872179407Sobrien msp3400c_setstereo(client, VIDEO_SOUND_MONO); 873179407Sobrien break; 874179407Sobrien } 875179407Sobrien 876179407Sobrien if (msp->watch_stereo) 877179407Sobrien watch_stereo(client); 878179407Sobrien 879179407Sobrien /* unmute + restore dfp registers */ 880179407Sobrien msp3400c_setvolume(client, msp->muted, msp->left, msp->right); 881179407Sobrien msp3400c_restore_dfp(client); 882179407Sobrien 883179407Sobrien if (bootverbose) 884179407Sobrien msp3400c_print_mode(msp); 885179407Sobrien 886179407Sobrien msp->active = 0; 887179407Sobrien } 888179407Sobrien 889179407Sobriendone: 890179407Sobrien dprintk("msp3400: thread: exit\n"); 891179407Sobrien msp->active = 0; 892179407Sobrien 893179407Sobrien msp->kthread = NULL; 894179407Sobrien wakeup(&msp->kthread); 895179407Sobrien 896179407Sobrien kthread_exit(0); 897179407Sobrien} 898179407Sobrien 899179407Sobrien/* ----------------------------------------------------------------------- */ 900179407Sobrien/* this one uses the automatic sound standard detection of newer */ 901179407Sobrien/* msp34xx chip versions */ 902179407Sobrien 903179407Sobrienstatic struct MODES { 904179407Sobrien int retval; 905179407Sobrien int main, second; 906179407Sobrien char *name; 907179407Sobrien} modelist[] = { 908179407Sobrien { 0x0000, 0, 0, "ERROR" }, 909179407Sobrien { 0x0001, 0, 0, "autodetect start" }, 910179407Sobrien { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" }, 911179407Sobrien { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" }, 912179407Sobrien { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" }, 913179407Sobrien { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" }, 914179407Sobrien { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" }, 915179407Sobrien { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" }, 916179407Sobrien { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" }, 917179407Sobrien { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" }, 918179407Sobrien { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" }, 919179407Sobrien { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" }, 920179407Sobrien { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" }, 921179407Sobrien { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" }, 922179407Sobrien { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" }, 923179407Sobrien { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" }, 924179407Sobrien { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" }, 925179407Sobrien { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" }, 926179407Sobrien { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, 927179407Sobrien { -1, 0, 0, NULL }, /* EOF */ 928179407Sobrien}; 929179407Sobrien 930179407Sobrienstatic void msp3410d_thread(void *data) 931179407Sobrien{ 932179407Sobrien bktr_ptr_t client = data; 933179407Sobrien struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 934179407Sobrien int mode,val,i,std; 935179407Sobrien int timo = 0; 936179407Sobrien 937179407Sobrien dprintk("msp3410: thread started\n"); 938179407Sobrien 939179407Sobrien for (;;) { 940179407Sobrien if (msp->rmmod) 941179407Sobrien goto done; 942179407Sobrien if (!msp->watch_stereo) 943179407Sobrien timo = 0; 944179407Sobrien else 945179407Sobrien timo = 10*hz; 946179407Sobrien tsleep(msp->kthread, PRIBIO, "idle", timo); 947179407Sobrien if (msp->rmmod) 948179407Sobrien goto done; 949179407Sobrien if (msp->halt_thread) { 950179407Sobrien msp->watch_stereo = 0; 951179407Sobrien msp->halt_thread = 0; 952179407Sobrien dprintk("msp3410: thread halted\n"); 953179407Sobrien continue; 954179407Sobrien } 955179407Sobrien 956179407Sobrien if (msp->mode == MSP_MODE_EXTERN) 957179407Sobrien continue; 958179407Sobrien 959179407Sobrien msp->active = 1; 960179407Sobrien 961179407Sobrien if (msp->watch_stereo) { 962179407Sobrien watch_stereo(client); 963179407Sobrien msp->active = 0; 964179407Sobrien continue; 965179407Sobrien } 966179407Sobrien 967179407Sobrien /* some time for the tuner to sync */ 968179407Sobrien tsleep(msp->kthread, PRIBIO, "tuner sync", hz/2); 969179407Sobrien 970179407Sobrien restart: 971179407Sobrien if (msp->mode == MSP_MODE_EXTERN) 972179407Sobrien continue; 973179407Sobrien msp->restart = 0; 974179407Sobrien msp->watch_stereo = 0; 975179407Sobrien 976179407Sobrien /* put into sane state (and mute) */ 977179407Sobrien msp3400c_reset(client); 978179407Sobrien 979179407Sobrien /* start autodetect */ 980218822Sdim switch (msp->norm) { 981218822Sdim case VIDEO_MODE_PAL: 982218822Sdim mode = 0x1003; 983218822Sdim std = 1; 984218822Sdim break; 985218822Sdim case VIDEO_MODE_NTSC: /* BTSC */ 986218822Sdim mode = 0x2003; 987218822Sdim std = 0x0020; 988218822Sdim break; 989218822Sdim case VIDEO_MODE_SECAM: 990218822Sdim mode = 0x0003; 991179407Sobrien std = 1; 992179407Sobrien break; 993179407Sobrien case VIDEO_MODE_RADIO: 994179407Sobrien mode = 0x0003; 995179407Sobrien std = 0x0040; 996179407Sobrien break; 997179407Sobrien default: 998179407Sobrien mode = 0x0003; 999179407Sobrien std = 1; 1000179407Sobrien break; 1001179407Sobrien } 1002179407Sobrien msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode); 1003179407Sobrien msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std); 1004179407Sobrien 1005179407Sobrien if (bootverbose) { 1006179407Sobrien int i; 1007179407Sobrien for (i = 0; modelist[i].name != NULL; i++) 1008179407Sobrien if (modelist[i].retval == std) 1009179407Sobrien break; 1010179407Sobrien dprintk("msp3410: setting mode: %s (0x%04x)\n", 1011179407Sobrien modelist[i].name ? modelist[i].name : "unknown",std); 1012179407Sobrien } 1013179407Sobrien 1014179407Sobrien if (std != 1) { 1015179407Sobrien /* programmed some specific mode */ 1016179407Sobrien val = std; 1017179407Sobrien } else { 1018179407Sobrien /* triggered autodetect */ 1019179407Sobrien for (;;) { 1020179407Sobrien tsleep(msp->kthread, PRIBIO, "autodetection", hz/10); 1021218822Sdim if (msp->restart) 1022218822Sdim goto restart; 1023218822Sdim 1024218822Sdim /* check results */ 1025218822Sdim val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); 1026218822Sdim if (val < 0x07ff) 1027218822Sdim break; 1028218822Sdim dprintk("msp3410: detection still in progress\n"); 1029218822Sdim } 1030218822Sdim } 1031179407Sobrien for (i = 0; modelist[i].name != NULL; i++) 1032179407Sobrien if (modelist[i].retval == val) 1033179407Sobrien break; 1034179407Sobrien dprintk("msp3410: current mode: %s (0x%04x)\n", 1035179407Sobrien modelist[i].name ? modelist[i].name : "unknown", 1036179407Sobrien val); 1037179407Sobrien msp->main = modelist[i].main; 1038179407Sobrien msp->second = modelist[i].second; 1039179407Sobrien 1040179407Sobrien if (client->amsound && (msp->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) { 1041179407Sobrien /* autodetection has failed, let backup */ 1042179407Sobrien dprintk("msp3410: autodetection failed, switching to backup mode: %s (0x%04x)\n", 1043179407Sobrien modelist[8].name ? modelist[8].name : "unknown",val); 1044179407Sobrien val = 0x0009; 1045179407Sobrien msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val); 1046179407Sobrien } 1047179407Sobrien 1048179407Sobrien /* set various prescales */ 1049179407Sobrien msp3400c_write(client, I2C_MSP3400C_DFP, 0x0d, 0x1900); /* scart */ 1050179407Sobrien msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */ 1051179407Sobrien msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* nicam */ 1052179407Sobrien 1053179407Sobrien /* set stereo */ 1054179407Sobrien switch (val) { 1055179407Sobrien case 0x0008: /* B/G NICAM */ 1056179407Sobrien case 0x000a: /* I NICAM */ 1057179407Sobrien if (val == 0x0008) 1058179407Sobrien msp->mode = MSP_MODE_FM_NICAM1; 1059179407Sobrien else 1060179407Sobrien msp->mode = MSP_MODE_FM_NICAM2; 1061179407Sobrien /* just turn on stereo */ 1062179407Sobrien msp->stereo = VIDEO_SOUND_STEREO; 1063179407Sobrien msp->nicam_on = 1; 1064218822Sdim msp->watch_stereo = 1; 1065179407Sobrien msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 1066179407Sobrien break; 1067179407Sobrien case 0x0009: 1068179407Sobrien msp->mode = MSP_MODE_AM_NICAM; 1069179407Sobrien msp->stereo = VIDEO_SOUND_MONO; 1070179407Sobrien msp->nicam_on = 1; 1071179407Sobrien msp3400c_setstereo(client,VIDEO_SOUND_MONO); 1072179407Sobrien msp->watch_stereo = 1; 1073179407Sobrien break; 1074179407Sobrien case 0x0020: /* BTSC */ 1075179407Sobrien /* just turn on stereo */ 1076179407Sobrien msp->mode = MSP_MODE_BTSC; 1077179407Sobrien msp->stereo = VIDEO_SOUND_STEREO; 1078179407Sobrien msp->nicam_on = 0; 1079179407Sobrien msp->watch_stereo = 1; 1080179407Sobrien msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 1081179407Sobrien break; 1082179407Sobrien case 0x0040: /* FM radio */ 1083179407Sobrien msp->mode = MSP_MODE_FM_RADIO; 1084179407Sobrien msp->stereo = VIDEO_SOUND_STEREO; 1085179407Sobrien msp->nicam_on = 0; 1086179407Sobrien msp->watch_stereo = 0; 1087179407Sobrien /* scart routing */ 1088179407Sobrien msp3400c_set_scart(client,SCART_IN2,0); 1089179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0220); 1090179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0220); 1091179407Sobrien msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0220); 1092179407Sobrien break; 1093179407Sobrien case 0x0003: 1094179407Sobrien msp->mode = MSP_MODE_FM_TERRA; 1095179407Sobrien msp->stereo = VIDEO_SOUND_STEREO; 1096179407Sobrien msp->nicam_on = 0; 1097179407Sobrien msp->watch_stereo = 1; 1098179407Sobrien msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 1099179407Sobrien break; 1100179407Sobrien } 1101179407Sobrien 1102179407Sobrien if (msp->watch_stereo) 1103179407Sobrien watch_stereo(client); 1104179407Sobrien 1105179407Sobrien /* unmute + restore dfp registers */ 1106179407Sobrien msp3400c_setbass(client, msp->bass); 1107179407Sobrien msp3400c_settreble(client, msp->treble); 1108179407Sobrien msp3400c_setvolume(client, msp->muted, msp->left, msp->right); 1109179407Sobrien msp3400c_restore_dfp(client); 1110179407Sobrien 1111179407Sobrien msp->active = 0; 1112179407Sobrien } 1113179407Sobrien 1114179407Sobriendone: 1115179407Sobrien dprintk("msp3410: thread: exit\n"); 1116179407Sobrien msp->active = 0; 1117179407Sobrien 1118179407Sobrien msp->kthread = NULL; 1119179407Sobrien wakeup(&msp->kthread); 1120179407Sobrien 1121179407Sobrien kthread_exit(0); 1122179407Sobrien} 1123179407Sobrien 1124179407Sobrienint msp_attach(bktr_ptr_t bktr) 1125179407Sobrien{ 1126179407Sobrien struct msp3400c *msp; 1127179407Sobrien int rev1,rev2,i; 1128179407Sobrien int err; 1129179407Sobrien char buf[20]; 1130179407Sobrien 1131179407Sobrien msp = (struct msp3400c *) malloc(sizeof(struct msp3400c), M_DEVBUF, M_NOWAIT); 1132179407Sobrien if (msp == NULL) 1133179407Sobrien return ENOMEM; 1134179407Sobrien bktr->msp3400c_info = msp; 1135179407Sobrien 1136179407Sobrien memset(msp,0,sizeof(struct msp3400c)); 1137218822Sdim msp->left = 65535; 1138179407Sobrien msp->right = 65535; 1139179407Sobrien msp->bass = 32768; 1140179407Sobrien msp->treble = 32768; 1141179407Sobrien msp->input = -1; 1142179407Sobrien msp->threaddesc = malloc(15 * sizeof(char), M_DEVBUF, M_NOWAIT); 1143179407Sobrien if (msp->threaddesc == NULL) { 1144179407Sobrien free(msp, M_DEVBUF); 1145179407Sobrien return ENOMEM; 1146179407Sobrien } 1147179407Sobrien snprintf(msp->threaddesc, 14, "%s_msp34xx_thread", bktr->bktr_xname); 1148179407Sobrien 1149179407Sobrien for (i = 0; i < DFP_COUNT; i++) 1150179407Sobrien msp->dfp_regs[i] = -1; 1151179407Sobrien 1152179407Sobrien msp3400c_reset(bktr); 1153179407Sobrien 1154179407Sobrien rev1 = msp3400c_read(bktr, I2C_MSP3400C_DFP, 0x1e); 1155179407Sobrien if (-1 != rev1) 1156179407Sobrien rev2 = msp3400c_read(bktr, I2C_MSP3400C_DFP, 0x1f); 1157179407Sobrien if ((-1 == rev1) || (0 == rev1 && 0 == rev2)) { 1158179407Sobrien free(msp->threaddesc, M_DEVBUF); 1159179407Sobrien free(msp, M_DEVBUF); 1160179407Sobrien bktr->msp3400c_info = NULL; 1161179407Sobrien printf("%s: msp3400: error while reading chip version\n", bktr_name(bktr)); 1162179407Sobrien return ENXIO; 1163218822Sdim } 1164218822Sdim 1165218822Sdim#if 0 1166218822Sdim /* this will turn on a 1kHz beep - might be useful for debugging... */ 1167218822Sdim msp3400c_write(bktr,I2C_MSP3400C_DFP, 0x0014, 0x1040); 1168218822Sdim#endif 1169218822Sdim 1170218822Sdim sprintf(buf,"MSP34%02d%c-%c%d", 1171179407Sobrien (rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); 1172218822Sdim msp->nicam = (((rev2>>8)&0xff) != 00) ? 1 : 0; 1173179407Sobrien 1174179407Sobrien if (bktr->mspsimple == -1) { 1175179407Sobrien /* default mode */ 1176179407Sobrien /* msp->simple = (((rev2>>8)&0xff) == 0) ? 0 : 1; */ 1177179407Sobrien msp->simple = ((rev1&0xff)+'@' > 'C'); 1178179407Sobrien } else { 1179179407Sobrien /* use kenv value */ 1180179407Sobrien msp->simple = bktr->mspsimple; 1181179407Sobrien } 1182179407Sobrien 1183179407Sobrien /* hello world :-) */ 1184179407Sobrien if (bootverbose) { 1185179407Sobrien printf("%s: msp34xx: init: chip=%s", bktr_name(bktr), buf); 1186179407Sobrien if (msp->nicam) 1187179407Sobrien printf(", has NICAM support"); 1188179407Sobrien printf("\n"); 1189179407Sobrien } 1190179407Sobrien 1191179407Sobrien /* startup control thread */ 1192179407Sobrien err = kthread_create(msp->simple ? msp3410d_thread : msp3400c_thread, 1193179407Sobrien bktr, &msp->kthread, (RFFDG | RFPROC), 0, 1194179407Sobrien msp->threaddesc); 1195218822Sdim if (err) { 1196179407Sobrien printf("%s: Error returned by kthread_create: %d", bktr_name(bktr), err); 1197179407Sobrien free(msp->threaddesc, M_DEVBUF); 1198179407Sobrien free(msp, M_DEVBUF); 1199179407Sobrien bktr->msp3400c_info = NULL; 1200179407Sobrien return ENXIO; 1201179407Sobrien } 1202179407Sobrien wakeup(msp->kthread); 1203179407Sobrien 1204179407Sobrien /* done */ 1205218822Sdim return 0; 1206218822Sdim} 1207218822Sdim 1208218822Sdimint msp_detach(bktr_ptr_t client) 1209218822Sdim{ 1210218822Sdim struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 1211218822Sdim 1212218822Sdim /* shutdown control thread */ 1213218822Sdim if (msp->kthread) 1214218822Sdim { 1215218822Sdim /* XXX mutex lock required */ 1216218822Sdim msp->rmmod = 1; 1217218822Sdim msp->watch_stereo = 0; 1218218822Sdim wakeup(msp->kthread); 1219179407Sobrien 1220179407Sobrien while (msp->kthread) 1221218822Sdim tsleep(&msp->kthread, PRIBIO, "wait for kthread", hz/10); 1222218822Sdim } 1223218822Sdim 1224218822Sdim if (client->msp3400c_info != NULL) { 1225218822Sdim free(client->msp3400c_info, M_DEVBUF); 1226218822Sdim client->msp3400c_info = NULL; 1227218822Sdim } 1228218822Sdim 1229179407Sobrien msp3400c_reset(client); 1230179407Sobrien 1231179407Sobrien return 0; 1232179407Sobrien} 1233179407Sobrien 1234179407Sobrienvoid msp_wake_thread(bktr_ptr_t client) 1235179407Sobrien{ 1236218822Sdim struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 1237179407Sobrien 1238179407Sobrien msp3400c_setvolume(client,msp->muted,0,0); 1239179407Sobrien msp->watch_stereo=0; 1240179407Sobrien if (msp->active) 1241179407Sobrien msp->restart = 1; 1242179407Sobrien wakeup(msp->kthread); 1243179407Sobrien} 1244179407Sobrien 1245218822Sdimvoid msp_halt_thread(bktr_ptr_t client) 1246218822Sdim{ 1247218822Sdim struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 1248218822Sdim 1249218822Sdim msp3400c_setvolume(client,msp->muted,0,0); 1250218822Sdim if (msp->active) 1251218822Sdim msp->restart = 1; 1252179407Sobrien msp->halt_thread = 1; 1253179407Sobrien wakeup(msp->kthread); 1254179407Sobrien} 1255179407Sobrien#endif /* BKTR_NEW_MSP34XX_DRIVER */ 1256179407Sobrien