msp34xx.c revision 118819
1118819Salex/*- 2118819Salex * Copyright (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org> 3118819Salex * All rights reserved. 4118819Salex * 5118819Salex * Redistribution and use in source and binary forms, with or without 6118819Salex * modification, are permitted provided that the following conditions 7118819Salex * are met: 8118819Salex * 1. Redistributions of source code must retain the above copyright 9118819Salex * notice, this list of conditions and the following disclaimer. 10118819Salex * 2. Redistributions in binary form must reproduce the above copyright 11118819Salex * notice, this list of conditions and the following disclaimer in the 12118819Salex * documentation and/or other materials provided with the distribution. 13118819Salex * 14118819Salex * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15118819Salex * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16118819Salex * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17118819Salex * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18118819Salex * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19118819Salex * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20118819Salex * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21118819Salex * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22118819Salex * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23118819Salex * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24118819Salex * SUCH DAMAGE. 25118819Salex * 26118819Salex * $FreeBSD: head/sys/dev/bktr/msp34xx.c 118819 2003-08-12 09:45:34Z alex $ 27118819Salex */ 28118819Salex 29118819Salex/* 30118819Salex * programming the msp34* sound processor family 31118819Salex * 32118819Salex * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org> 33118819Salex * 34118819Salex * what works and what doesn't: 35118819Salex * 36118819Salex * AM-Mono 37118819Salex * Support for Hauppauge cards added (decoding handled by tuner) added by 38118819Salex * Frederic Crozat <fcrozat@mail.dotcom.fr> 39118819Salex * 40118819Salex * FM-Mono 41118819Salex * should work. The stereo modes are backward compatible to FM-mono, 42118819Salex * therefore FM-Mono should be allways available. 43118819Salex * 44118819Salex * FM-Stereo (B/G, used in germany) 45118819Salex * should work, with autodetect 46118819Salex * 47118819Salex * FM-Stereo (satellite) 48118819Salex * should work, no autodetect (i.e. default is mono, but you can 49118819Salex * switch to stereo -- untested) 50118819Salex * 51118819Salex * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) 52118819Salex * should work, with autodetect. Support for NICAM was added by 53118819Salex * Pekka Pietikainen <pp@netppl.fi> 54118819Salex * 55118819Salex * 56118819Salex * TODO: 57118819Salex * - better SAT support 58118819Salex * 59118819Salex * 60118819Salex * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch) 61118819Salex * using soundcore instead of OSS 62118819Salex * 63118819Salex * 64118819Salex * The FreeBSD modifications by Alexander Langer <alex@FreeBSD.org> 65118819Salex * are in the public domain. Please contact me (Alex) and not Gerd for 66118819Salex * any problems you encounter under FreeBSD. 67118819Salex * 68118819Salex * FreeBSD TODO: 69118819Salex * - mutex handling (currently not mp-safe) 70118819Salex * - the various options here as loader tunables or compile time or whatever 71118819Salex * - how does the new dolby flag work with the current dpl_* stuff? 72118819Salex * Maybe it's just enough to set the dolby flag to 1 and it works. 73118819Salex * As I don't have a dolby card myself, I can't test it, though. 74118819Salex */ 75118819Salex 76118819Salex#include "opt_bktr.h" /* Include any kernel config options */ 77118819Salex#ifdef BKTR_NEW_MSP34XX_DRIVER /* file only needed for new driver */ 78118819Salex 79118819Salex#include <sys/param.h> 80118819Salex#include <sys/systm.h> 81118819Salex#include <sys/kernel.h> 82118819Salex#include <sys/vnode.h> 83118819Salex 84118819Salex#include <sys/unistd.h> 85118819Salex#include <sys/kthread.h> 86118819Salex#include <sys/malloc.h> 87118819Salex 88118819Salex#include <machine/bus.h> /* required by bktr_reg.h */ 89118819Salex 90118819Salex#include <machine/ioctl_meteor.h> 91118819Salex#include <machine/ioctl_bt848.h> /* extensions to ioctl_meteor.h */ 92118819Salex#include <dev/bktr/bktr_reg.h> 93118819Salex#include <dev/bktr/bktr_tuner.h> 94118819Salex#include <dev/bktr/bktr_audio.h> 95118819Salex#include <dev/bktr/bktr_core.h> 96118819Salex 97118819Salex#define VIDEO_MODE_PAL 0 98118819Salex#define VIDEO_MODE_NTSC 1 99118819Salex#define VIDEO_MODE_SECAM 2 100118819Salex#define VIDEO_MODE_AUTO 3 101118819Salex 102118819Salex#define VIDEO_SOUND_MONO 1 103118819Salex#define VIDEO_SOUND_STEREO 2 104118819Salex#define VIDEO_SOUND_LANG1 4 105118819Salex#define VIDEO_SOUND_LANG2 8 106118819Salex 107118819Salex#define DFP_COUNT 0x41 108118819Salexstatic const int bl_dfp[] = { 109118819Salex 0x00, 0x01, 0x02, 0x03, 0x06, 0x08, 0x09, 0x0a, 110118819Salex 0x0b, 0x0d, 0x0e, 0x10 111118819Salex}; 112118819Salex 113118819Salexstruct msp3400c { 114118819Salex int simple; 115118819Salex int nicam; 116118819Salex int mode; 117118819Salex int norm; 118118819Salex int stereo; 119118819Salex int nicam_on; 120118819Salex int acb; 121118819Salex int main, second; /* sound carrier */ 122118819Salex int input; 123118819Salex 124118819Salex int muted; 125118819Salex int left, right; /* volume */ 126118819Salex int bass, treble; 127118819Salex 128118819Salex /* shadow register set */ 129118819Salex int dfp_regs[DFP_COUNT]; 130118819Salex 131118819Salex /* thread */ 132118819Salex struct proc *kthread; 133118819Salex char *threaddesc; 134118819Salex 135118819Salex int active,restart,rmmod; 136118819Salex 137118819Salex int watch_stereo; 138118819Salex int halt_thread; 139118819Salex}; 140118819Salex 141118819Salex#define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ 142118819Salex 143118819Salex/* ---------------------------------------------------------------------- */ 144118819Salex 145118819Salex#define dprintk(...) do { \ 146118819Salex if (bootverbose) { \ 147118819Salex printf("%s: ", bktr_name(client)); \ 148118819Salex printf(__VA_ARGS__); \ 149118819Salex } \ 150118819Salex} while (0) 151118819Salex 152118819Salex/* ---------------------------------------------------------------------- */ 153118819Salex 154118819Salex#define I2C_MSP3400C 0x80 155118819Salex#define I2C_MSP3400C_DEM 0x10 156118819Salex#define I2C_MSP3400C_DFP 0x12 157118819Salex 158118819Salex/* ----------------------------------------------------------------------- */ 159118819Salex/* functions for talking to the MSP3400C Sound processor */ 160118819Salex 161118819Salexstatic int msp3400c_reset(bktr_ptr_t client) 162118819Salex{ 163118819Salex /* use our own which handles config(8) options */ 164118819Salex msp_dpl_reset(client, client->msp_addr); 165118819Salex 166118819Salex return 0; 167118819Salex} 168118819Salex 169118819Salexstatic int 170118819Salexmsp3400c_read(bktr_ptr_t client, int dev, int addr) 171118819Salex{ 172118819Salex /* use our own */ 173118819Salex return(msp_dpl_read(client, client->msp_addr, dev, addr)); 174118819Salex} 175118819Salex 176118819Salexstatic int 177118819Salexmsp3400c_write(bktr_ptr_t client, int dev, int addr, int val) 178118819Salex{ 179118819Salex /* use our own */ 180118819Salex msp_dpl_write(client, client->msp_addr, dev, addr, val); 181118819Salex 182118819Salex return(0); 183118819Salex} 184118819Salex 185118819Salex/* ------------------------------------------------------------------------ */ 186118819Salex 187118819Salex/* This macro is allowed for *constants* only, gcc must calculate it 188118819Salex at compile time. Remember -- no floats in kernel mode */ 189118819Salex#define MSP_CARRIER(freq) ((int)((float)(freq/18.432)*(1<<24))) 190118819Salex 191118819Salex#define MSP_MODE_AM_DETECT 0 192118819Salex#define MSP_MODE_FM_RADIO 2 193118819Salex#define MSP_MODE_FM_TERRA 3 194118819Salex#define MSP_MODE_FM_SAT 4 195118819Salex#define MSP_MODE_FM_NICAM1 5 196118819Salex#define MSP_MODE_FM_NICAM2 6 197118819Salex#define MSP_MODE_AM_NICAM 7 198118819Salex#define MSP_MODE_BTSC 8 199118819Salex#define MSP_MODE_EXTERN 9 200118819Salex 201118819Salexstatic struct MSP_INIT_DATA_DEM { 202118819Salex int fir1[6]; 203118819Salex int fir2[6]; 204118819Salex int cdo1; 205118819Salex int cdo2; 206118819Salex int ad_cv; 207118819Salex int mode_reg; 208118819Salex int dfp_src; 209118819Salex int dfp_matrix; 210118819Salex} msp_init_data[] = { 211118819Salex /* AM (for carrier detect / msp3400) */ 212118819Salex { { 75, 19, 36, 35, 39, 40 }, { 75, 19, 36, 35, 39, 40 }, 213118819Salex MSP_CARRIER(5.5), MSP_CARRIER(5.5), 214118819Salex 0x00d0, 0x0500, 0x0020, 0x3000}, 215118819Salex 216118819Salex /* AM (for carrier detect / msp3410) */ 217118819Salex { { -1, -1, -8, 2, 59, 126 }, { -1, -1, -8, 2, 59, 126 }, 218118819Salex MSP_CARRIER(5.5), MSP_CARRIER(5.5), 219118819Salex 0x00d0, 0x0100, 0x0020, 0x3000}, 220118819Salex 221118819Salex /* FM Radio */ 222118819Salex { { -8, -8, 4, 6, 78, 107 }, { -8, -8, 4, 6, 78, 107 }, 223118819Salex MSP_CARRIER(10.7), MSP_CARRIER(10.7), 224118819Salex 0x00d0, 0x0480, 0x0020, 0x3000 }, 225118819Salex 226118819Salex /* Terrestial FM-mono + FM-stereo */ 227118819Salex { { 3, 18, 27, 48, 66, 72 }, { 3, 18, 27, 48, 66, 72 }, 228118819Salex MSP_CARRIER(5.5), MSP_CARRIER(5.5), 229118819Salex 0x00d0, 0x0480, 0x0030, 0x3000}, 230118819Salex 231118819Salex /* Sat FM-mono */ 232118819Salex { { 1, 9, 14, 24, 33, 37 }, { 3, 18, 27, 48, 66, 72 }, 233118819Salex MSP_CARRIER(6.5), MSP_CARRIER(6.5), 234118819Salex 0x00c6, 0x0480, 0x0000, 0x3000}, 235118819Salex 236118819Salex /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ 237118819Salex { { -2, -8, -10, 10, 50, 86 }, { 3, 18, 27, 48, 66, 72 }, 238118819Salex MSP_CARRIER(5.5), MSP_CARRIER(5.5), 239118819Salex 0x00d0, 0x0040, 0x0120, 0x3000}, 240118819Salex 241118819Salex /* NICAM/FM -- I (6.0/6.552) */ 242118819Salex { { 2, 4, -6, -4, 40, 94 }, { 3, 18, 27, 48, 66, 72 }, 243118819Salex MSP_CARRIER(6.0), MSP_CARRIER(6.0), 244118819Salex 0x00d0, 0x0040, 0x0120, 0x3000}, 245118819Salex 246118819Salex /* NICAM/AM -- L (6.5/5.85) */ 247118819Salex { { -2, -8, -10, 10, 50, 86 }, { -4, -12, -9, 23, 79, 126 }, 248118819Salex MSP_CARRIER(6.5), MSP_CARRIER(6.5), 249118819Salex 0x00c6, 0x0140, 0x0120, 0x7c03}, 250118819Salex}; 251118819Salex 252118819Salexstruct CARRIER_DETECT { 253118819Salex int cdo; 254118819Salex char *name; 255118819Salex}; 256118819Salex 257118819Salexstatic struct CARRIER_DETECT carrier_detect_main[] = { 258118819Salex /* main carrier */ 259118819Salex { MSP_CARRIER(4.5), "4.5 NTSC" }, 260118819Salex { MSP_CARRIER(5.5), "5.5 PAL B/G" }, 261118819Salex { MSP_CARRIER(6.0), "6.0 PAL I" }, 262118819Salex { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } 263118819Salex}; 264118819Salex 265118819Salexstatic struct CARRIER_DETECT carrier_detect_55[] = { 266118819Salex /* PAL B/G */ 267118819Salex { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, 268118819Salex { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } 269118819Salex}; 270118819Salex 271118819Salexstatic struct CARRIER_DETECT carrier_detect_65[] = { 272118819Salex /* PAL SAT / SECAM */ 273118819Salex { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, 274118819Salex { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, 275118819Salex { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, 276118819Salex { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, 277118819Salex { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, 278118819Salex { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, 279118819Salex}; 280118819Salex 281118819Salex#define CARRIER_COUNT(x) (sizeof(x)/sizeof(struct CARRIER_DETECT)) 282118819Salex 283118819Salex/* ----------------------------------------------------------------------- */ 284118819Salex 285118819Salex#define SCART_MASK 0 286118819Salex#define SCART_IN1 1 287118819Salex#define SCART_IN2 2 288118819Salex#define SCART_IN1_DA 3 289118819Salex#define SCART_IN2_DA 4 290118819Salex#define SCART_IN3 5 291118819Salex#define SCART_IN4 6 292118819Salex#define SCART_MONO 7 293118819Salex#define SCART_MUTE 8 294118819Salex 295118819Salexstatic int scarts[3][9] = { 296118819Salex /* MASK IN1 IN2 IN1_DA IN2_DA IN3 IN4 MONO MUTE */ 297118819Salex { 0x0320, 0x0000, 0x0200, -1, -1, 0x0300, 0x0020, 0x0100, 0x0320 }, 298118819Salex { 0x0c40, 0x0440, 0x0400, 0x0c00, 0x0040, 0x0000, 0x0840, 0x0800, 0x0c40 }, 299118819Salex { 0x3080, 0x1000, 0x1080, 0x0000, 0x0080, 0x2080, 0x3080, 0x2000, 0x3000 }, 300118819Salex}; 301118819Salex 302118819Salexstatic char *scart_names[] = { 303118819Salex "mask", "in1", "in2", "in1 da", "in2 da", "in3", "in4", "mono", "mute" 304118819Salex}; 305118819Salex 306118819Salexstatic void 307118819Salexmsp3400c_set_scart(bktr_ptr_t client, int in, int out) 308118819Salex{ 309118819Salex struct msp3400c *msp = client->msp3400c_info; 310118819Salex 311118819Salex if (-1 == scarts[out][in]) 312118819Salex return; 313118819Salex 314118819Salex dprintk("msp34xx: scart switch: %s => %d\n",scart_names[in],out); 315118819Salex msp->acb &= ~scarts[out][SCART_MASK]; 316118819Salex msp->acb |= scarts[out][in]; 317118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0013, msp->acb); 318118819Salex} 319118819Salex 320118819Salex/* ------------------------------------------------------------------------ */ 321118819Salex 322118819Salexstatic void msp3400c_setcarrier(bktr_ptr_t client, int cdo1, int cdo2) 323118819Salex{ 324118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0093, cdo1 & 0xfff); 325118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12); 326118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff); 327118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12); 328118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ 329118819Salex} 330118819Salex 331118819Salexstatic void msp3400c_setvolume(bktr_ptr_t client, 332118819Salex int muted, int left, int right) 333118819Salex{ 334118819Salex int vol = 0,val = 0,balance = 0; 335118819Salex 336118819Salex if (!muted) { 337118819Salex vol = (left > right) ? left : right; 338118819Salex val = (vol * 0x73 / 65535) << 8; 339118819Salex } 340118819Salex if (vol > 0) { 341118819Salex balance = ((right-left) * 127) / vol; 342118819Salex } 343118819Salex 344118819Salex dprintk("msp34xx: setvolume: mute=%s %d:%d v=0x%02x b=0x%02x\n", 345118819Salex muted ? "on" : "off", left, right, val>>8, balance); 346118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ 347118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ 348118819Salex /* scart - on/off only */ 349118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); 350118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0001, balance << 8); 351118819Salex} 352118819Salex 353118819Salexstatic void msp3400c_setbass(bktr_ptr_t client, int bass) 354118819Salex{ 355118819Salex int val = ((bass-32768) * 0x60 / 65535) << 8; 356118819Salex 357118819Salex dprintk("msp34xx: setbass: %d 0x%02x\n",bass, val>>8); 358118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */ 359118819Salex} 360118819Salex 361118819Salexstatic void msp3400c_settreble(bktr_ptr_t client, int treble) 362118819Salex{ 363118819Salex int val = ((treble-32768) * 0x60 / 65535) << 8; 364118819Salex 365118819Salex dprintk("msp34xx: settreble: %d 0x%02x\n",treble, val>>8); 366118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */ 367118819Salex} 368118819Salex 369118819Salexstatic void msp3400c_setmode(bktr_ptr_t client, int type) 370118819Salex{ 371118819Salex struct msp3400c *msp = client->msp3400c_info; 372118819Salex int i; 373118819Salex 374118819Salex dprintk("msp3400: setmode: %d\n",type); 375118819Salex msp->mode = type; 376118819Salex msp->stereo = VIDEO_SOUND_MONO; 377118819Salex 378118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ 379118819Salex msp_init_data[type].ad_cv); 380118819Salex 381118819Salex for (i = 5; i >= 0; i--) /* fir 1 */ 382118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0001, 383118819Salex msp_init_data[type].fir1[i]); 384118819Salex 385118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */ 386118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0040); 387118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0000); 388118819Salex for (i = 5; i >= 0; i--) 389118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 390118819Salex msp_init_data[type].fir2[i]); 391118819Salex 392118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */ 393118819Salex msp_init_data[type].mode_reg); 394118819Salex 395118819Salex msp3400c_setcarrier(client, msp_init_data[type].cdo1, 396118819Salex msp_init_data[type].cdo2); 397118819Salex 398118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ 399118819Salex 400118819Salex if (client->dolby) { 401118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, 402118819Salex 0x0520); /* I2S1 */ 403118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, 404118819Salex 0x0620); /* I2S2 */ 405118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, 406118819Salex msp_init_data[type].dfp_src); 407118819Salex } else { 408118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, 409118819Salex msp_init_data[type].dfp_src); 410118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, 411118819Salex msp_init_data[type].dfp_src); 412118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, 413118819Salex msp_init_data[type].dfp_src); 414118819Salex } 415118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a, 416118819Salex msp_init_data[type].dfp_src); 417118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 418118819Salex msp_init_data[type].dfp_matrix); 419118819Salex 420118819Salex if (msp->nicam) { 421118819Salex /* nicam prescale */ 422118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x5a00); /* was: 0x3000 */ 423118819Salex } 424118819Salex} 425118819Salex 426118819Salex/* turn on/off nicam + stereo */ 427118819Salexstatic void msp3400c_setstereo(bktr_ptr_t client, int mode) 428118819Salex{ 429118819Salex static char *strmode[] = { "0", "mono", "stereo", "3", 430118819Salex "lang1", "5", "6", "7", "lang2" }; 431118819Salex struct msp3400c *msp = client->msp3400c_info; 432118819Salex int nicam=0; /* channel source: FM/AM or nicam */ 433118819Salex int src=0; 434118819Salex 435118819Salex /* switch demodulator */ 436118819Salex switch (msp->mode) { 437118819Salex case MSP_MODE_FM_TERRA: 438118819Salex dprintk("msp3400: FM setstereo: %s\n",strmode[mode]); 439118819Salex msp3400c_setcarrier(client,msp->second,msp->main); 440118819Salex switch (mode) { 441118819Salex case VIDEO_SOUND_STEREO: 442118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3001); 443118819Salex break; 444118819Salex case VIDEO_SOUND_MONO: 445118819Salex case VIDEO_SOUND_LANG1: 446118819Salex case VIDEO_SOUND_LANG2: 447118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3000); 448118819Salex break; 449118819Salex } 450118819Salex break; 451118819Salex case MSP_MODE_FM_SAT: 452118819Salex dprintk("msp3400: SAT setstereo: %s\n",strmode[mode]); 453118819Salex switch (mode) { 454118819Salex case VIDEO_SOUND_MONO: 455118819Salex msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); 456118819Salex break; 457118819Salex case VIDEO_SOUND_STEREO: 458118819Salex msp3400c_setcarrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); 459118819Salex break; 460118819Salex case VIDEO_SOUND_LANG1: 461118819Salex msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); 462118819Salex break; 463118819Salex case VIDEO_SOUND_LANG2: 464118819Salex msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); 465118819Salex break; 466118819Salex } 467118819Salex break; 468118819Salex case MSP_MODE_FM_NICAM1: 469118819Salex case MSP_MODE_FM_NICAM2: 470118819Salex case MSP_MODE_AM_NICAM: 471118819Salex dprintk("msp3400: NICAM setstereo: %s\n",strmode[mode]); 472118819Salex msp3400c_setcarrier(client,msp->second,msp->main); 473118819Salex if (msp->nicam_on) 474118819Salex nicam=0x0100; 475118819Salex break; 476118819Salex case MSP_MODE_BTSC: 477118819Salex dprintk("msp3400: BTSC setstereo: %s\n",strmode[mode]); 478118819Salex nicam=0x0300; 479118819Salex break; 480118819Salex case MSP_MODE_EXTERN: 481118819Salex dprintk("msp3400: extern setstereo: %s\n",strmode[mode]); 482118819Salex nicam = 0x0200; 483118819Salex break; 484118819Salex case MSP_MODE_FM_RADIO: 485118819Salex dprintk("msp3400: FM-Radio setstereo: %s\n",strmode[mode]); 486118819Salex break; 487118819Salex default: 488118819Salex dprintk("msp3400: mono setstereo\n"); 489118819Salex return; 490118819Salex } 491118819Salex 492118819Salex /* switch audio */ 493118819Salex switch (mode) { 494118819Salex case VIDEO_SOUND_STEREO: 495118819Salex src = 0x0020 | nicam; 496118819Salex#if 0 497118819Salex /* spatial effect */ 498118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0005,0x4000); 499118819Salex#endif 500118819Salex break; 501118819Salex case VIDEO_SOUND_MONO: 502118819Salex if (msp->mode == MSP_MODE_AM_NICAM) { 503118819Salex dprintk("msp3400: switching to AM mono\n"); 504118819Salex /* AM mono decoding is handled by tuner, not MSP chip */ 505118819Salex /* SCART switching control register */ 506118819Salex msp3400c_set_scart(client,SCART_MONO,0); 507118819Salex src = 0x0200; 508118819Salex break; 509118819Salex } 510118819Salex case VIDEO_SOUND_LANG1: 511118819Salex src = 0x0000 | nicam; 512118819Salex break; 513118819Salex case VIDEO_SOUND_LANG2: 514118819Salex src = 0x0010 | nicam; 515118819Salex break; 516118819Salex } 517118819Salex dprintk("msp3400: setstereo final source/matrix = 0x%x\n", src); 518118819Salex 519118819Salex if (client->dolby) { 520118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,0x0520); 521118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,0x0620); 522118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); 523118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); 524118819Salex } else { 525118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,src); 526118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,src); 527118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); 528118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); 529118819Salex } 530118819Salex} 531118819Salex 532118819Salexstatic void 533118819Salexmsp3400c_print_mode(struct msp3400c *msp) 534118819Salex{ 535118819Salex if (msp->main == msp->second) { 536118819Salex printf("bktr: msp3400: mono sound carrier: %d.%03d MHz\n", 537118819Salex msp->main/910000,(msp->main/910)%1000); 538118819Salex } else { 539118819Salex printf("bktr: msp3400: main sound carrier: %d.%03d MHz\n", 540118819Salex msp->main/910000,(msp->main/910)%1000); 541118819Salex } 542118819Salex if (msp->mode == MSP_MODE_FM_NICAM1 || 543118819Salex msp->mode == MSP_MODE_FM_NICAM2) 544118819Salex printf("bktr: msp3400: NICAM/FM carrier : %d.%03d MHz\n", 545118819Salex msp->second/910000,(msp->second/910)%1000); 546118819Salex if (msp->mode == MSP_MODE_AM_NICAM) 547118819Salex printf("bktr: msp3400: NICAM/AM carrier : %d.%03d MHz\n", 548118819Salex msp->second/910000,(msp->second/910)%1000); 549118819Salex if (msp->mode == MSP_MODE_FM_TERRA && 550118819Salex msp->main != msp->second) { 551118819Salex printf("bktr: msp3400: FM-stereo carrier : %d.%03d MHz\n", 552118819Salex msp->second/910000,(msp->second/910)%1000); 553118819Salex } 554118819Salex} 555118819Salex 556118819Salexstatic void 557118819Salexmsp3400c_restore_dfp(bktr_ptr_t client) 558118819Salex{ 559118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 560118819Salex int i; 561118819Salex 562118819Salex for (i = 0; i < DFP_COUNT; i++) { 563118819Salex if (-1 == msp->dfp_regs[i]) 564118819Salex continue; 565118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, i, msp->dfp_regs[i]); 566118819Salex } 567118819Salex} 568118819Salex 569118819Salex/* ----------------------------------------------------------------------- */ 570118819Salex 571118819Salexstruct REGISTER_DUMP { 572118819Salex int addr; 573118819Salex char *name; 574118819Salex}; 575118819Salex 576118819Salexstruct REGISTER_DUMP d1[] = { 577118819Salex { 0x007e, "autodetect" }, 578118819Salex { 0x0023, "C_AD_BITS " }, 579118819Salex { 0x0038, "ADD_BITS " }, 580118819Salex { 0x003e, "CIB_BITS " }, 581118819Salex { 0x0057, "ERROR_RATE" }, 582118819Salex}; 583118819Salex 584118819Salexstatic int 585118819Salexautodetect_stereo(bktr_ptr_t client) 586118819Salex{ 587118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 588118819Salex int val; 589118819Salex int newstereo = msp->stereo; 590118819Salex int newnicam = msp->nicam_on; 591118819Salex int update = 0; 592118819Salex 593118819Salex switch (msp->mode) { 594118819Salex case MSP_MODE_FM_TERRA: 595118819Salex val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18); 596118819Salex if (val > 32767) 597118819Salex val -= 65536; 598118819Salex dprintk("msp34xx: stereo detect register: %d\n",val); 599118819Salex 600118819Salex if (val > 4096) { 601118819Salex newstereo = VIDEO_SOUND_STEREO | VIDEO_SOUND_MONO; 602118819Salex } else if (val < -4096) { 603118819Salex newstereo = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; 604118819Salex } else { 605118819Salex newstereo = VIDEO_SOUND_MONO; 606118819Salex } 607118819Salex newnicam = 0; 608118819Salex break; 609118819Salex case MSP_MODE_FM_NICAM1: 610118819Salex case MSP_MODE_FM_NICAM2: 611118819Salex case MSP_MODE_AM_NICAM: 612118819Salex val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x23); 613118819Salex dprintk("msp34xx: nicam sync=%d, mode=%d\n",val & 1, (val & 0x1e) >> 1); 614118819Salex 615118819Salex if (val & 1) { 616118819Salex /* nicam synced */ 617118819Salex switch ((val & 0x1e) >> 1) { 618118819Salex case 0: 619118819Salex case 8: 620118819Salex newstereo = VIDEO_SOUND_STEREO; 621118819Salex break; 622118819Salex case 1: 623118819Salex case 9: 624118819Salex newstereo = VIDEO_SOUND_MONO 625118819Salex | VIDEO_SOUND_LANG1; 626118819Salex break; 627118819Salex case 2: 628118819Salex case 10: 629118819Salex newstereo = VIDEO_SOUND_MONO 630118819Salex | VIDEO_SOUND_LANG1 631118819Salex | VIDEO_SOUND_LANG2; 632118819Salex break; 633118819Salex default: 634118819Salex newstereo = VIDEO_SOUND_MONO; 635118819Salex break; 636118819Salex } 637118819Salex newnicam=1; 638118819Salex } else { 639118819Salex newnicam = 0; 640118819Salex newstereo = VIDEO_SOUND_MONO; 641118819Salex } 642118819Salex break; 643118819Salex case MSP_MODE_BTSC: 644118819Salex val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200); 645118819Salex dprintk("msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n", 646118819Salex val, 647118819Salex (val & 0x0002) ? "no" : "yes", 648118819Salex (val & 0x0004) ? "no" : "yes", 649118819Salex (val & 0x0040) ? "stereo" : "mono", 650118819Salex (val & 0x0080) ? ", nicam 2nd mono" : "", 651118819Salex (val & 0x0100) ? ", bilingual/SAP" : ""); 652118819Salex newstereo = VIDEO_SOUND_MONO; 653118819Salex if (val & 0x0040) newstereo |= VIDEO_SOUND_STEREO; 654118819Salex if (val & 0x0100) newstereo |= VIDEO_SOUND_LANG1; 655118819Salex break; 656118819Salex } 657118819Salex if (newstereo != msp->stereo) { 658118819Salex update = 1; 659118819Salex dprintk("msp34xx: watch: stereo %d => %d\n", 660118819Salex msp->stereo,newstereo); 661118819Salex msp->stereo = newstereo; 662118819Salex } 663118819Salex if (newnicam != msp->nicam_on) { 664118819Salex update = 1; 665118819Salex dprintk("msp34xx: watch: nicam %d => %d\n", 666118819Salex msp->nicam_on,newnicam); 667118819Salex msp->nicam_on = newnicam; 668118819Salex } 669118819Salex return update; 670118819Salex} 671118819Salex 672118819Salex/* 673118819Salex * A kernel thread for msp3400 control -- we don't want to block the 674118819Salex * in the ioctl while doing the sound carrier & stereo detect 675118819Salex */ 676118819Salex 677118819Salex/* stereo/multilang monitoring */ 678118819Salexstatic void watch_stereo(bktr_ptr_t client) 679118819Salex{ 680118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 681118819Salex 682118819Salex if (autodetect_stereo(client)) { 683118819Salex if (msp->stereo & VIDEO_SOUND_STEREO) 684118819Salex msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 685118819Salex else if (msp->stereo & VIDEO_SOUND_LANG1) 686118819Salex msp3400c_setstereo(client,VIDEO_SOUND_LANG1); 687118819Salex else 688118819Salex msp3400c_setstereo(client,VIDEO_SOUND_MONO); 689118819Salex } 690118819Salex if (client->stereo_once) 691118819Salex msp->watch_stereo = 0; 692118819Salex 693118819Salex} 694118819Salex 695118819Salexstatic void msp3400c_thread(void *data) 696118819Salex{ 697118819Salex bktr_ptr_t client = data; 698118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 699118819Salex 700118819Salex struct CARRIER_DETECT *cd; 701118819Salex int count, max1,max2,val1,val2, val,this; 702118819Salex 703118819Salex dprintk("msp3400: thread started\n"); 704118819Salex 705118819Salex for (;;) { 706118819Salex if (msp->rmmod) 707118819Salex goto done; 708118819Salex tsleep(msp->kthread, PRIBIO, "idle", 0); 709118819Salex if (msp->rmmod) 710118819Salex goto done; 711118819Salex if (msp->halt_thread) { 712118819Salex msp->watch_stereo = 0; 713118819Salex msp->halt_thread = 0; 714118819Salex continue; 715118819Salex } 716118819Salex 717118819Salex if (VIDEO_MODE_RADIO == msp->norm || 718118819Salex MSP_MODE_EXTERN == msp->mode) 719118819Salex continue; /* nothing to do */ 720118819Salex 721118819Salex msp->active = 1; 722118819Salex 723118819Salex if (msp->watch_stereo) { 724118819Salex watch_stereo(client); 725118819Salex msp->active = 0; 726118819Salex continue; 727118819Salex } 728118819Salex 729118819Salex /* some time for the tuner to sync */ 730118819Salex tsleep(msp->kthread, PRIBIO, "tuner sync", hz/2); 731118819Salex 732118819Salex restart: 733118819Salex if (VIDEO_MODE_RADIO == msp->norm || 734118819Salex MSP_MODE_EXTERN == msp->mode) 735118819Salex continue; /* nothing to do */ 736118819Salex msp->restart = 0; 737118819Salex msp3400c_setvolume(client, msp->muted, 0, 0); 738118819Salex msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); 739118819Salex val1 = val2 = 0; 740118819Salex max1 = max2 = -1; 741118819Salex msp->watch_stereo = 0; 742118819Salex 743118819Salex /* carrier detect pass #1 -- main carrier */ 744118819Salex cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main); 745118819Salex 746118819Salex if (client->amsound && (msp->norm == VIDEO_MODE_SECAM)) { 747118819Salex /* autodetect doesn't work well with AM ... */ 748118819Salex max1 = 3; 749118819Salex count = 0; 750118819Salex dprintk("msp3400: AM sound override\n"); 751118819Salex } 752118819Salex 753118819Salex for (this = 0; this < count; this++) { 754118819Salex msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); 755118819Salex 756118819Salex tsleep(msp->kthread, PRIBIO, "carrier detect", hz/100); 757118819Salex 758118819Salex if (msp->restart) 759118819Salex msp->restart = 0; 760118819Salex 761118819Salex val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); 762118819Salex if (val > 32767) 763118819Salex val -= 65536; 764118819Salex if (val1 < val) 765118819Salex val1 = val, max1 = this; 766118819Salex dprintk("msp3400: carrier1 val: %5d / %s\n", val,cd[this].name); 767118819Salex } 768118819Salex 769118819Salex /* carrier detect pass #2 -- second (stereo) carrier */ 770118819Salex switch (max1) { 771118819Salex case 1: /* 5.5 */ 772118819Salex cd = carrier_detect_55; count = CARRIER_COUNT(carrier_detect_55); 773118819Salex break; 774118819Salex case 3: /* 6.5 */ 775118819Salex cd = carrier_detect_65; count = CARRIER_COUNT(carrier_detect_65); 776118819Salex break; 777118819Salex case 0: /* 4.5 */ 778118819Salex case 2: /* 6.0 */ 779118819Salex default: 780118819Salex cd = NULL; count = 0; 781118819Salex break; 782118819Salex } 783118819Salex 784118819Salex if (client->amsound && (msp->norm == VIDEO_MODE_SECAM)) { 785118819Salex /* autodetect doesn't work well with AM ... */ 786118819Salex cd = NULL; count = 0; max2 = 0; 787118819Salex } 788118819Salex for (this = 0; this < count; this++) { 789118819Salex msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); 790118819Salex 791118819Salex tsleep(msp->kthread, PRIBIO, "carrier detection", hz/100); 792118819Salex if (msp->restart) 793118819Salex goto restart; 794118819Salex 795118819Salex val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); 796118819Salex if (val > 32767) 797118819Salex val -= 65536; 798118819Salex if (val2 < val) 799118819Salex val2 = val, max2 = this; 800118819Salex dprintk("msp3400: carrier2 val: %5d / %s\n", val,cd[this].name); 801118819Salex } 802118819Salex 803118819Salex /* programm the msp3400 according to the results */ 804118819Salex msp->main = carrier_detect_main[max1].cdo; 805118819Salex switch (max1) { 806118819Salex case 1: /* 5.5 */ 807118819Salex if (max2 == 0) { 808118819Salex /* B/G FM-stereo */ 809118819Salex msp->second = carrier_detect_55[max2].cdo; 810118819Salex msp3400c_setmode(client, MSP_MODE_FM_TERRA); 811118819Salex msp->nicam_on = 0; 812118819Salex /* XXX why mono? this probably can do stereo... - Alex*/ 813118819Salex msp3400c_setstereo(client, VIDEO_SOUND_MONO); 814118819Salex msp->watch_stereo = 1; 815118819Salex } else if (max2 == 1 && msp->nicam) { 816118819Salex /* B/G NICAM */ 817118819Salex msp->second = carrier_detect_55[max2].cdo; 818118819Salex msp3400c_setmode(client, MSP_MODE_FM_NICAM1); 819118819Salex msp->nicam_on = 1; 820118819Salex msp3400c_setcarrier(client, msp->second, msp->main); 821118819Salex msp->watch_stereo = 1; 822118819Salex } else { 823118819Salex goto no_second; 824118819Salex } 825118819Salex break; 826118819Salex case 2: /* 6.0 */ 827118819Salex /* PAL I NICAM */ 828118819Salex msp->second = MSP_CARRIER(6.552); 829118819Salex msp3400c_setmode(client, MSP_MODE_FM_NICAM2); 830118819Salex msp->nicam_on = 1; 831118819Salex msp3400c_setcarrier(client, msp->second, msp->main); 832118819Salex msp->watch_stereo = 1; 833118819Salex break; 834118819Salex case 3: /* 6.5 */ 835118819Salex if (max2 == 1 || max2 == 2) { 836118819Salex /* D/K FM-stereo */ 837118819Salex msp->second = carrier_detect_65[max2].cdo; 838118819Salex msp3400c_setmode(client, MSP_MODE_FM_TERRA); 839118819Salex msp->nicam_on = 0; 840118819Salex msp3400c_setstereo(client, VIDEO_SOUND_MONO); 841118819Salex msp->watch_stereo = 1; 842118819Salex } else if (max2 == 0 && 843118819Salex msp->norm == VIDEO_MODE_SECAM) { 844118819Salex /* L NICAM or AM-mono */ 845118819Salex msp->second = carrier_detect_65[max2].cdo; 846118819Salex msp3400c_setmode(client, MSP_MODE_AM_NICAM); 847118819Salex msp->nicam_on = 0; 848118819Salex msp3400c_setstereo(client, VIDEO_SOUND_MONO); 849118819Salex msp3400c_setcarrier(client, msp->second, msp->main); 850118819Salex /* volume prescale for SCART (AM mono input) */ 851118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000d, 0x1900); 852118819Salex msp->watch_stereo = 1; 853118819Salex } else if (max2 == 0 && msp->nicam) { 854118819Salex /* D/K NICAM */ 855118819Salex msp->second = carrier_detect_65[max2].cdo; 856118819Salex msp3400c_setmode(client, MSP_MODE_FM_NICAM1); 857118819Salex msp->nicam_on = 1; 858118819Salex msp3400c_setcarrier(client, msp->second, msp->main); 859118819Salex msp->watch_stereo = 1; 860118819Salex } else { 861118819Salex goto no_second; 862118819Salex } 863118819Salex break; 864118819Salex case 0: /* 4.5 */ 865118819Salex default: 866118819Salex no_second: 867118819Salex msp->second = carrier_detect_main[max1].cdo; 868118819Salex msp3400c_setmode(client, MSP_MODE_FM_TERRA); 869118819Salex msp->nicam_on = 0; 870118819Salex msp3400c_setcarrier(client, msp->second, msp->main); 871118819Salex msp->stereo = VIDEO_SOUND_MONO; 872118819Salex msp3400c_setstereo(client, VIDEO_SOUND_MONO); 873118819Salex break; 874118819Salex } 875118819Salex 876118819Salex if (msp->watch_stereo) 877118819Salex watch_stereo(client); 878118819Salex 879118819Salex /* unmute + restore dfp registers */ 880118819Salex msp3400c_setvolume(client, msp->muted, msp->left, msp->right); 881118819Salex msp3400c_restore_dfp(client); 882118819Salex 883118819Salex if (bootverbose) 884118819Salex msp3400c_print_mode(msp); 885118819Salex 886118819Salex msp->active = 0; 887118819Salex } 888118819Salex 889118819Salexdone: 890118819Salex dprintk("msp3400: thread: exit\n"); 891118819Salex msp->active = 0; 892118819Salex 893118819Salex msp->kthread = NULL; 894118819Salex wakeup(&msp->kthread); 895118819Salex 896118819Salex kthread_exit(0); 897118819Salex} 898118819Salex 899118819Salex/* ----------------------------------------------------------------------- */ 900118819Salex/* this one uses the automatic sound standard detection of newer */ 901118819Salex/* msp34xx chip versions */ 902118819Salex 903118819Salexstatic struct MODES { 904118819Salex int retval; 905118819Salex int main, second; 906118819Salex char *name; 907118819Salex} modelist[] = { 908118819Salex { 0x0000, 0, 0, "ERROR" }, 909118819Salex { 0x0001, 0, 0, "autodetect start" }, 910118819Salex { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" }, 911118819Salex { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" }, 912118819Salex { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" }, 913118819Salex { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" }, 914118819Salex { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" }, 915118819Salex { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" }, 916118819Salex { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" }, 917118819Salex { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" }, 918118819Salex { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" }, 919118819Salex { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" }, 920118819Salex { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" }, 921118819Salex { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" }, 922118819Salex { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" }, 923118819Salex { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" }, 924118819Salex { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" }, 925118819Salex { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" }, 926118819Salex { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, 927118819Salex { -1, 0, 0, NULL }, /* EOF */ 928118819Salex}; 929118819Salex 930118819Salexstatic void msp3410d_thread(void *data) 931118819Salex{ 932118819Salex bktr_ptr_t client = data; 933118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 934118819Salex int mode,val,i,std; 935118819Salex int timo = 0; 936118819Salex 937118819Salex dprintk("msp3410: thread started\n"); 938118819Salex 939118819Salex for (;;) { 940118819Salex if (msp->rmmod) 941118819Salex goto done; 942118819Salex if (!msp->watch_stereo) 943118819Salex timo = 0; 944118819Salex else 945118819Salex timo = 10*hz; 946118819Salex tsleep(msp->kthread, PRIBIO, "idle", timo); 947118819Salex if (msp->rmmod) 948118819Salex goto done; 949118819Salex if (msp->halt_thread) { 950118819Salex msp->watch_stereo = 0; 951118819Salex msp->halt_thread = 0; 952118819Salex dprintk("msp3410: thread halted\n"); 953118819Salex continue; 954118819Salex } 955118819Salex 956118819Salex if (msp->mode == MSP_MODE_EXTERN) 957118819Salex continue; 958118819Salex 959118819Salex msp->active = 1; 960118819Salex 961118819Salex if (msp->watch_stereo) { 962118819Salex watch_stereo(client); 963118819Salex msp->active = 0; 964118819Salex continue; 965118819Salex } 966118819Salex 967118819Salex /* some time for the tuner to sync */ 968118819Salex tsleep(msp->kthread, PRIBIO, "tuner sync", hz/2); 969118819Salex 970118819Salex restart: 971118819Salex if (msp->mode == MSP_MODE_EXTERN) 972118819Salex continue; 973118819Salex msp->restart = 0; 974118819Salex msp->watch_stereo = 0; 975118819Salex 976118819Salex /* put into sane state (and mute) */ 977118819Salex msp3400c_reset(client); 978118819Salex 979118819Salex /* start autodetect */ 980118819Salex switch (msp->norm) { 981118819Salex case VIDEO_MODE_PAL: 982118819Salex mode = 0x1003; 983118819Salex std = 1; 984118819Salex break; 985118819Salex case VIDEO_MODE_NTSC: /* BTSC */ 986118819Salex mode = 0x2003; 987118819Salex std = 0x0020; 988118819Salex break; 989118819Salex case VIDEO_MODE_SECAM: 990118819Salex mode = 0x0003; 991118819Salex std = 1; 992118819Salex break; 993118819Salex case VIDEO_MODE_RADIO: 994118819Salex mode = 0x0003; 995118819Salex std = 0x0040; 996118819Salex break; 997118819Salex default: 998118819Salex mode = 0x0003; 999118819Salex std = 1; 1000118819Salex break; 1001118819Salex } 1002118819Salex msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode); 1003118819Salex msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std); 1004118819Salex 1005118819Salex if (bootverbose) { 1006118819Salex int i; 1007118819Salex for (i = 0; modelist[i].name != NULL; i++) 1008118819Salex if (modelist[i].retval == std) 1009118819Salex break; 1010118819Salex dprintk("msp3410: setting mode: %s (0x%04x)\n", 1011118819Salex modelist[i].name ? modelist[i].name : "unknown",std); 1012118819Salex } 1013118819Salex 1014118819Salex if (std != 1) { 1015118819Salex /* programmed some specific mode */ 1016118819Salex val = std; 1017118819Salex } else { 1018118819Salex /* triggered autodetect */ 1019118819Salex for (;;) { 1020118819Salex tsleep(msp->kthread, PRIBIO, "autodetection", hz/10); 1021118819Salex if (msp->restart) 1022118819Salex goto restart; 1023118819Salex 1024118819Salex /* check results */ 1025118819Salex val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); 1026118819Salex if (val < 0x07ff) 1027118819Salex break; 1028118819Salex dprintk("msp3410: detection still in progress\n"); 1029118819Salex } 1030118819Salex } 1031118819Salex for (i = 0; modelist[i].name != NULL; i++) 1032118819Salex if (modelist[i].retval == val) 1033118819Salex break; 1034118819Salex dprintk("msp3410: current mode: %s (0x%04x)\n", 1035118819Salex modelist[i].name ? modelist[i].name : "unknown", 1036118819Salex val); 1037118819Salex msp->main = modelist[i].main; 1038118819Salex msp->second = modelist[i].second; 1039118819Salex 1040118819Salex if (client->amsound && (msp->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) { 1041118819Salex /* autodetection has failed, let backup */ 1042118819Salex dprintk("msp3410: autodetection failed, switching to backup mode: %s (0x%04x)\n", 1043118819Salex modelist[8].name ? modelist[8].name : "unknown",val); 1044118819Salex val = 0x0009; 1045118819Salex msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val); 1046118819Salex } 1047118819Salex 1048118819Salex /* set various prescales */ 1049118819Salex msp3400c_write(client, I2C_MSP3400C_DFP, 0x0d, 0x1900); /* scart */ 1050118819Salex msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */ 1051118819Salex msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* nicam */ 1052118819Salex 1053118819Salex /* set stereo */ 1054118819Salex switch (val) { 1055118819Salex case 0x0008: /* B/G NICAM */ 1056118819Salex case 0x000a: /* I NICAM */ 1057118819Salex if (val == 0x0008) 1058118819Salex msp->mode = MSP_MODE_FM_NICAM1; 1059118819Salex else 1060118819Salex msp->mode = MSP_MODE_FM_NICAM2; 1061118819Salex /* just turn on stereo */ 1062118819Salex msp->stereo = VIDEO_SOUND_STEREO; 1063118819Salex msp->nicam_on = 1; 1064118819Salex msp->watch_stereo = 1; 1065118819Salex msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 1066118819Salex break; 1067118819Salex case 0x0009: 1068118819Salex msp->mode = MSP_MODE_AM_NICAM; 1069118819Salex msp->stereo = VIDEO_SOUND_MONO; 1070118819Salex msp->nicam_on = 1; 1071118819Salex msp3400c_setstereo(client,VIDEO_SOUND_MONO); 1072118819Salex msp->watch_stereo = 1; 1073118819Salex break; 1074118819Salex case 0x0020: /* BTSC */ 1075118819Salex /* just turn on stereo */ 1076118819Salex msp->mode = MSP_MODE_BTSC; 1077118819Salex msp->stereo = VIDEO_SOUND_STEREO; 1078118819Salex msp->nicam_on = 0; 1079118819Salex msp->watch_stereo = 1; 1080118819Salex msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 1081118819Salex break; 1082118819Salex case 0x0040: /* FM radio */ 1083118819Salex msp->mode = MSP_MODE_FM_RADIO; 1084118819Salex msp->stereo = VIDEO_SOUND_STEREO; 1085118819Salex msp->nicam_on = 0; 1086118819Salex msp->watch_stereo = 0; 1087118819Salex /* scart routing */ 1088118819Salex msp3400c_set_scart(client,SCART_IN2,0); 1089118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0220); 1090118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0220); 1091118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0220); 1092118819Salex break; 1093118819Salex case 0x0003: 1094118819Salex msp->mode = MSP_MODE_FM_TERRA; 1095118819Salex msp->stereo = VIDEO_SOUND_STEREO; 1096118819Salex msp->nicam_on = 0; 1097118819Salex msp->watch_stereo = 1; 1098118819Salex msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 1099118819Salex break; 1100118819Salex } 1101118819Salex 1102118819Salex if (msp->watch_stereo) 1103118819Salex watch_stereo(client); 1104118819Salex 1105118819Salex /* unmute + restore dfp registers */ 1106118819Salex msp3400c_setbass(client, msp->bass); 1107118819Salex msp3400c_settreble(client, msp->treble); 1108118819Salex msp3400c_setvolume(client, msp->muted, msp->left, msp->right); 1109118819Salex msp3400c_restore_dfp(client); 1110118819Salex 1111118819Salex msp->active = 0; 1112118819Salex } 1113118819Salex 1114118819Salexdone: 1115118819Salex dprintk("msp3410: thread: exit\n"); 1116118819Salex msp->active = 0; 1117118819Salex 1118118819Salex msp->kthread = NULL; 1119118819Salex wakeup(&msp->kthread); 1120118819Salex 1121118819Salex kthread_exit(0); 1122118819Salex} 1123118819Salex 1124118819Salexint msp_attach(bktr_ptr_t bktr) 1125118819Salex{ 1126118819Salex struct msp3400c *msp; 1127118819Salex int rev1,rev2,i; 1128118819Salex int err; 1129118819Salex char buf[20]; 1130118819Salex 1131118819Salex msp = (struct msp3400c *) malloc(sizeof(struct msp3400c), M_DEVBUF, M_NOWAIT); 1132118819Salex if (msp == NULL) 1133118819Salex return ENOMEM; 1134118819Salex bktr->msp3400c_info = msp; 1135118819Salex 1136118819Salex memset(msp,0,sizeof(struct msp3400c)); 1137118819Salex msp->left = 65535; 1138118819Salex msp->right = 65535; 1139118819Salex msp->bass = 32768; 1140118819Salex msp->treble = 32768; 1141118819Salex msp->input = -1; 1142118819Salex msp->threaddesc = malloc(15 * sizeof(char), M_DEVBUF, M_NOWAIT); 1143118819Salex if (msp->threaddesc == NULL) { 1144118819Salex free(msp, M_DEVBUF); 1145118819Salex return ENOMEM; 1146118819Salex } 1147118819Salex snprintf(msp->threaddesc, 14, "%s_msp34xx_thread", bktr->bktr_xname); 1148118819Salex 1149118819Salex for (i = 0; i < DFP_COUNT; i++) 1150118819Salex msp->dfp_regs[i] = -1; 1151118819Salex 1152118819Salex msp3400c_reset(bktr); 1153118819Salex 1154118819Salex rev1 = msp3400c_read(bktr, I2C_MSP3400C_DFP, 0x1e); 1155118819Salex if (-1 != rev1) 1156118819Salex rev2 = msp3400c_read(bktr, I2C_MSP3400C_DFP, 0x1f); 1157118819Salex if ((-1 == rev1) || (0 == rev1 && 0 == rev2)) { 1158118819Salex free(msp->threaddesc, M_DEVBUF); 1159118819Salex free(msp, M_DEVBUF); 1160118819Salex bktr->msp3400c_info = NULL; 1161118819Salex printf("%s: msp3400: error while reading chip version\n", bktr_name(bktr)); 1162118819Salex return ENXIO; 1163118819Salex } 1164118819Salex 1165118819Salex#if 0 1166118819Salex /* this will turn on a 1kHz beep - might be useful for debugging... */ 1167118819Salex msp3400c_write(bktr,I2C_MSP3400C_DFP, 0x0014, 0x1040); 1168118819Salex#endif 1169118819Salex 1170118819Salex sprintf(buf,"MSP34%02d%c-%c%d", 1171118819Salex (rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); 1172118819Salex msp->nicam = (((rev2>>8)&0xff) != 00) ? 1 : 0; 1173118819Salex 1174118819Salex if (bktr->mspsimple == -1) { 1175118819Salex /* default mode */ 1176118819Salex /* msp->simple = (((rev2>>8)&0xff) == 0) ? 0 : 1; */ 1177118819Salex msp->simple = ((rev1&0xff)+'@' > 'C'); 1178118819Salex } else { 1179118819Salex /* use kenv value */ 1180118819Salex msp->simple = bktr->mspsimple; 1181118819Salex } 1182118819Salex 1183118819Salex /* hello world :-) */ 1184118819Salex if (bootverbose) { 1185118819Salex printf("%s: msp34xx: init: chip=%s", bktr_name(bktr), buf); 1186118819Salex if (msp->nicam) 1187118819Salex printf(", has NICAM support"); 1188118819Salex printf("\n"); 1189118819Salex } 1190118819Salex 1191118819Salex /* startup control thread */ 1192118819Salex err = kthread_create(msp->simple ? msp3410d_thread : msp3400c_thread, 1193118819Salex bktr, &msp->kthread, (RFFDG | RFPROC), 0, 1194118819Salex msp->threaddesc); 1195118819Salex if (err) { 1196118819Salex printf("%s: Error returned by kthread_create: %d", bktr_name(bktr), err); 1197118819Salex free(msp->threaddesc, M_DEVBUF); 1198118819Salex free(msp, M_DEVBUF); 1199118819Salex bktr->msp3400c_info = NULL; 1200118819Salex return ENXIO; 1201118819Salex } 1202118819Salex wakeup(msp->kthread); 1203118819Salex 1204118819Salex /* done */ 1205118819Salex return 0; 1206118819Salex} 1207118819Salex 1208118819Salexint msp_detach(bktr_ptr_t client) 1209118819Salex{ 1210118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 1211118819Salex 1212118819Salex /* shutdown control thread */ 1213118819Salex if (msp->kthread) 1214118819Salex { 1215118819Salex /* XXX mutex lock required */ 1216118819Salex msp->rmmod = 1; 1217118819Salex msp->watch_stereo = 0; 1218118819Salex wakeup(msp->kthread); 1219118819Salex 1220118819Salex while (msp->kthread) 1221118819Salex tsleep(&msp->kthread, PRIBIO, "wait for kthread", hz/10); 1222118819Salex } 1223118819Salex 1224118819Salex if (client->msp3400c_info != NULL) { 1225118819Salex free(client->msp3400c_info, M_DEVBUF); 1226118819Salex client->msp3400c_info = NULL; 1227118819Salex } 1228118819Salex 1229118819Salex msp3400c_reset(client); 1230118819Salex 1231118819Salex return 0; 1232118819Salex} 1233118819Salex 1234118819Salexvoid msp_wake_thread(bktr_ptr_t client) 1235118819Salex{ 1236118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 1237118819Salex 1238118819Salex msp3400c_setvolume(client,msp->muted,0,0); 1239118819Salex msp->watch_stereo=0; 1240118819Salex if (msp->active) 1241118819Salex msp->restart = 1; 1242118819Salex wakeup(msp->kthread); 1243118819Salex} 1244118819Salex 1245118819Salexvoid msp_halt_thread(bktr_ptr_t client) 1246118819Salex{ 1247118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 1248118819Salex 1249118819Salex msp3400c_setvolume(client,msp->muted,0,0); 1250118819Salex if (msp->active) 1251118819Salex msp->restart = 1; 1252118819Salex msp->halt_thread = 1; 1253118819Salex wakeup(msp->kthread); 1254118819Salex} 1255118819Salex#endif /* BKTR_NEW_MSP34XX_DRIVER */ 1256