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: stable/10/sys/dev/bktr/msp34xx.c 332920 2018-04-24 10:02:17Z riggs $ 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 88135069Sdwhite#ifdef BKTR_USE_FREEBSD_SMBUS 89135069Sdwhite#include <sys/bus.h> /* required by bktr_reg.h */ 90135069Sdwhite#endif 91135069Sdwhite 92118819Salex#include <machine/bus.h> /* required by bktr_reg.h */ 93118819Salex 94123497Srwatson#include <dev/bktr/ioctl_meteor.h> 95123497Srwatson#include <dev/bktr/ioctl_bt848.h> /* extensions to ioctl_meteor.h */ 96118819Salex#include <dev/bktr/bktr_reg.h> 97118819Salex#include <dev/bktr/bktr_tuner.h> 98118819Salex#include <dev/bktr/bktr_audio.h> 99118819Salex#include <dev/bktr/bktr_core.h> 100118819Salex 101118819Salex#define VIDEO_MODE_PAL 0 102118819Salex#define VIDEO_MODE_NTSC 1 103118819Salex#define VIDEO_MODE_SECAM 2 104118819Salex#define VIDEO_MODE_AUTO 3 105118819Salex 106118819Salex#define VIDEO_SOUND_MONO 1 107118819Salex#define VIDEO_SOUND_STEREO 2 108118819Salex#define VIDEO_SOUND_LANG1 4 109118819Salex#define VIDEO_SOUND_LANG2 8 110118819Salex 111118819Salex#define DFP_COUNT 0x41 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 134118819Salex int active,restart,rmmod; 135118819Salex 136118819Salex int watch_stereo; 137118819Salex int halt_thread; 138118819Salex}; 139118819Salex 140118819Salex#define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ 141118819Salex 142118819Salex/* ---------------------------------------------------------------------- */ 143118819Salex 144118819Salex#define dprintk(...) do { \ 145118819Salex if (bootverbose) { \ 146118819Salex printf("%s: ", bktr_name(client)); \ 147118819Salex printf(__VA_ARGS__); \ 148118819Salex } \ 149118819Salex} while (0) 150118819Salex 151118819Salex/* ---------------------------------------------------------------------- */ 152118819Salex 153118819Salex#define I2C_MSP3400C 0x80 154118819Salex#define I2C_MSP3400C_DEM 0x10 155118819Salex#define I2C_MSP3400C_DFP 0x12 156118819Salex 157118819Salex/* ----------------------------------------------------------------------- */ 158118819Salex/* functions for talking to the MSP3400C Sound processor */ 159118819Salex 160118819Salexstatic int msp3400c_reset(bktr_ptr_t client) 161118819Salex{ 162118819Salex /* use our own which handles config(8) options */ 163118819Salex msp_dpl_reset(client, client->msp_addr); 164118819Salex 165118819Salex return 0; 166118819Salex} 167118819Salex 168118819Salexstatic int 169118819Salexmsp3400c_read(bktr_ptr_t client, int dev, int addr) 170118819Salex{ 171118819Salex /* use our own */ 172118819Salex return(msp_dpl_read(client, client->msp_addr, dev, addr)); 173118819Salex} 174118819Salex 175118819Salexstatic int 176118819Salexmsp3400c_write(bktr_ptr_t client, int dev, int addr, int val) 177118819Salex{ 178118819Salex /* use our own */ 179118819Salex msp_dpl_write(client, client->msp_addr, dev, addr, val); 180118819Salex 181118819Salex return(0); 182118819Salex} 183118819Salex 184118819Salex/* ------------------------------------------------------------------------ */ 185118819Salex 186118819Salex/* This macro is allowed for *constants* only, gcc must calculate it 187118819Salex at compile time. Remember -- no floats in kernel mode */ 188118819Salex#define MSP_CARRIER(freq) ((int)((float)(freq/18.432)*(1<<24))) 189118819Salex 190118819Salex#define MSP_MODE_AM_DETECT 0 191118819Salex#define MSP_MODE_FM_RADIO 2 192118819Salex#define MSP_MODE_FM_TERRA 3 193118819Salex#define MSP_MODE_FM_SAT 4 194118819Salex#define MSP_MODE_FM_NICAM1 5 195118819Salex#define MSP_MODE_FM_NICAM2 6 196118819Salex#define MSP_MODE_AM_NICAM 7 197118819Salex#define MSP_MODE_BTSC 8 198118819Salex#define MSP_MODE_EXTERN 9 199118819Salex 200118819Salexstatic struct MSP_INIT_DATA_DEM { 201118819Salex int fir1[6]; 202118819Salex int fir2[6]; 203118819Salex int cdo1; 204118819Salex int cdo2; 205118819Salex int ad_cv; 206118819Salex int mode_reg; 207118819Salex int dfp_src; 208118819Salex int dfp_matrix; 209118819Salex} msp_init_data[] = { 210118819Salex /* AM (for carrier detect / msp3400) */ 211118819Salex { { 75, 19, 36, 35, 39, 40 }, { 75, 19, 36, 35, 39, 40 }, 212118819Salex MSP_CARRIER(5.5), MSP_CARRIER(5.5), 213118819Salex 0x00d0, 0x0500, 0x0020, 0x3000}, 214118819Salex 215118819Salex /* AM (for carrier detect / msp3410) */ 216118819Salex { { -1, -1, -8, 2, 59, 126 }, { -1, -1, -8, 2, 59, 126 }, 217118819Salex MSP_CARRIER(5.5), MSP_CARRIER(5.5), 218118819Salex 0x00d0, 0x0100, 0x0020, 0x3000}, 219118819Salex 220118819Salex /* FM Radio */ 221118819Salex { { -8, -8, 4, 6, 78, 107 }, { -8, -8, 4, 6, 78, 107 }, 222118819Salex MSP_CARRIER(10.7), MSP_CARRIER(10.7), 223118819Salex 0x00d0, 0x0480, 0x0020, 0x3000 }, 224118819Salex 225118819Salex /* Terrestial FM-mono + FM-stereo */ 226118819Salex { { 3, 18, 27, 48, 66, 72 }, { 3, 18, 27, 48, 66, 72 }, 227118819Salex MSP_CARRIER(5.5), MSP_CARRIER(5.5), 228118819Salex 0x00d0, 0x0480, 0x0030, 0x3000}, 229118819Salex 230118819Salex /* Sat FM-mono */ 231118819Salex { { 1, 9, 14, 24, 33, 37 }, { 3, 18, 27, 48, 66, 72 }, 232118819Salex MSP_CARRIER(6.5), MSP_CARRIER(6.5), 233118819Salex 0x00c6, 0x0480, 0x0000, 0x3000}, 234118819Salex 235118819Salex /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ 236118819Salex { { -2, -8, -10, 10, 50, 86 }, { 3, 18, 27, 48, 66, 72 }, 237118819Salex MSP_CARRIER(5.5), MSP_CARRIER(5.5), 238118819Salex 0x00d0, 0x0040, 0x0120, 0x3000}, 239118819Salex 240118819Salex /* NICAM/FM -- I (6.0/6.552) */ 241118819Salex { { 2, 4, -6, -4, 40, 94 }, { 3, 18, 27, 48, 66, 72 }, 242118819Salex MSP_CARRIER(6.0), MSP_CARRIER(6.0), 243118819Salex 0x00d0, 0x0040, 0x0120, 0x3000}, 244118819Salex 245118819Salex /* NICAM/AM -- L (6.5/5.85) */ 246118819Salex { { -2, -8, -10, 10, 50, 86 }, { -4, -12, -9, 23, 79, 126 }, 247118819Salex MSP_CARRIER(6.5), MSP_CARRIER(6.5), 248118819Salex 0x00c6, 0x0140, 0x0120, 0x7c03}, 249118819Salex}; 250118819Salex 251118819Salexstruct CARRIER_DETECT { 252118819Salex int cdo; 253118819Salex char *name; 254118819Salex}; 255118819Salex 256118819Salexstatic struct CARRIER_DETECT carrier_detect_main[] = { 257118819Salex /* main carrier */ 258118819Salex { MSP_CARRIER(4.5), "4.5 NTSC" }, 259118819Salex { MSP_CARRIER(5.5), "5.5 PAL B/G" }, 260118819Salex { MSP_CARRIER(6.0), "6.0 PAL I" }, 261118819Salex { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } 262118819Salex}; 263118819Salex 264118819Salexstatic struct CARRIER_DETECT carrier_detect_55[] = { 265118819Salex /* PAL B/G */ 266118819Salex { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, 267118819Salex { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } 268118819Salex}; 269118819Salex 270118819Salexstatic struct CARRIER_DETECT carrier_detect_65[] = { 271118819Salex /* PAL SAT / SECAM */ 272118819Salex { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, 273118819Salex { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, 274118819Salex { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, 275118819Salex { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, 276118819Salex { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, 277118819Salex { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, 278118819Salex}; 279118819Salex 280118819Salex#define CARRIER_COUNT(x) (sizeof(x)/sizeof(struct CARRIER_DETECT)) 281118819Salex 282118819Salex/* ----------------------------------------------------------------------- */ 283118819Salex 284118819Salex#define SCART_MASK 0 285118819Salex#define SCART_IN1 1 286118819Salex#define SCART_IN2 2 287118819Salex#define SCART_IN1_DA 3 288118819Salex#define SCART_IN2_DA 4 289118819Salex#define SCART_IN3 5 290118819Salex#define SCART_IN4 6 291118819Salex#define SCART_MONO 7 292118819Salex#define SCART_MUTE 8 293118819Salex 294118819Salexstatic int scarts[3][9] = { 295118819Salex /* MASK IN1 IN2 IN1_DA IN2_DA IN3 IN4 MONO MUTE */ 296118819Salex { 0x0320, 0x0000, 0x0200, -1, -1, 0x0300, 0x0020, 0x0100, 0x0320 }, 297118819Salex { 0x0c40, 0x0440, 0x0400, 0x0c00, 0x0040, 0x0000, 0x0840, 0x0800, 0x0c40 }, 298118819Salex { 0x3080, 0x1000, 0x1080, 0x0000, 0x0080, 0x2080, 0x3080, 0x2000, 0x3000 }, 299118819Salex}; 300118819Salex 301118819Salexstatic char *scart_names[] = { 302118819Salex "mask", "in1", "in2", "in1 da", "in2 da", "in3", "in4", "mono", "mute" 303118819Salex}; 304118819Salex 305118819Salexstatic void 306118819Salexmsp3400c_set_scart(bktr_ptr_t client, int in, int out) 307118819Salex{ 308118819Salex struct msp3400c *msp = client->msp3400c_info; 309118819Salex 310118819Salex if (-1 == scarts[out][in]) 311118819Salex return; 312118819Salex 313118819Salex dprintk("msp34xx: scart switch: %s => %d\n",scart_names[in],out); 314118819Salex msp->acb &= ~scarts[out][SCART_MASK]; 315118819Salex msp->acb |= scarts[out][in]; 316118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0013, msp->acb); 317118819Salex} 318118819Salex 319118819Salex/* ------------------------------------------------------------------------ */ 320118819Salex 321118819Salexstatic void msp3400c_setcarrier(bktr_ptr_t client, int cdo1, int cdo2) 322118819Salex{ 323118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0093, cdo1 & 0xfff); 324118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12); 325118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff); 326118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12); 327118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ 328118819Salex} 329118819Salex 330118819Salexstatic void msp3400c_setvolume(bktr_ptr_t client, 331118819Salex int muted, int left, int right) 332118819Salex{ 333118819Salex int vol = 0,val = 0,balance = 0; 334118819Salex 335118819Salex if (!muted) { 336118819Salex vol = (left > right) ? left : right; 337118819Salex val = (vol * 0x73 / 65535) << 8; 338118819Salex } 339118819Salex if (vol > 0) { 340118819Salex balance = ((right-left) * 127) / vol; 341118819Salex } 342118819Salex 343118819Salex dprintk("msp34xx: setvolume: mute=%s %d:%d v=0x%02x b=0x%02x\n", 344118819Salex muted ? "on" : "off", left, right, val>>8, balance); 345118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ 346118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ 347118819Salex /* scart - on/off only */ 348118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); 349118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0001, balance << 8); 350118819Salex} 351118819Salex 352118819Salexstatic void msp3400c_setbass(bktr_ptr_t client, int bass) 353118819Salex{ 354118819Salex int val = ((bass-32768) * 0x60 / 65535) << 8; 355118819Salex 356118819Salex dprintk("msp34xx: setbass: %d 0x%02x\n",bass, val>>8); 357118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */ 358118819Salex} 359118819Salex 360118819Salexstatic void msp3400c_settreble(bktr_ptr_t client, int treble) 361118819Salex{ 362118819Salex int val = ((treble-32768) * 0x60 / 65535) << 8; 363118819Salex 364118819Salex dprintk("msp34xx: settreble: %d 0x%02x\n",treble, val>>8); 365118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */ 366118819Salex} 367118819Salex 368118819Salexstatic void msp3400c_setmode(bktr_ptr_t client, int type) 369118819Salex{ 370118819Salex struct msp3400c *msp = client->msp3400c_info; 371118819Salex int i; 372118819Salex 373118819Salex dprintk("msp3400: setmode: %d\n",type); 374118819Salex msp->mode = type; 375118819Salex msp->stereo = VIDEO_SOUND_MONO; 376118819Salex 377118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ 378118819Salex msp_init_data[type].ad_cv); 379118819Salex 380118819Salex for (i = 5; i >= 0; i--) /* fir 1 */ 381118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0001, 382118819Salex msp_init_data[type].fir1[i]); 383118819Salex 384118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */ 385118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0040); 386118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0000); 387118819Salex for (i = 5; i >= 0; i--) 388118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 389118819Salex msp_init_data[type].fir2[i]); 390118819Salex 391118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */ 392118819Salex msp_init_data[type].mode_reg); 393118819Salex 394118819Salex msp3400c_setcarrier(client, msp_init_data[type].cdo1, 395118819Salex msp_init_data[type].cdo2); 396118819Salex 397118819Salex msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ 398118819Salex 399118819Salex if (client->dolby) { 400118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, 401118819Salex 0x0520); /* I2S1 */ 402118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, 403118819Salex 0x0620); /* I2S2 */ 404118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, 405118819Salex msp_init_data[type].dfp_src); 406118819Salex } else { 407118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, 408118819Salex msp_init_data[type].dfp_src); 409118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, 410118819Salex msp_init_data[type].dfp_src); 411118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, 412118819Salex msp_init_data[type].dfp_src); 413118819Salex } 414118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a, 415118819Salex msp_init_data[type].dfp_src); 416118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 417118819Salex msp_init_data[type].dfp_matrix); 418118819Salex 419118819Salex if (msp->nicam) { 420118819Salex /* nicam prescale */ 421118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x5a00); /* was: 0x3000 */ 422118819Salex } 423118819Salex} 424118819Salex 425118819Salex/* turn on/off nicam + stereo */ 426118819Salexstatic void msp3400c_setstereo(bktr_ptr_t client, int mode) 427118819Salex{ 428118819Salex static char *strmode[] = { "0", "mono", "stereo", "3", 429118819Salex "lang1", "5", "6", "7", "lang2" }; 430118819Salex struct msp3400c *msp = client->msp3400c_info; 431118819Salex int nicam=0; /* channel source: FM/AM or nicam */ 432118819Salex int src=0; 433118819Salex 434118819Salex /* switch demodulator */ 435118819Salex switch (msp->mode) { 436118819Salex case MSP_MODE_FM_TERRA: 437118819Salex dprintk("msp3400: FM setstereo: %s\n",strmode[mode]); 438118819Salex msp3400c_setcarrier(client,msp->second,msp->main); 439118819Salex switch (mode) { 440118819Salex case VIDEO_SOUND_STEREO: 441118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3001); 442118819Salex break; 443118819Salex case VIDEO_SOUND_MONO: 444118819Salex case VIDEO_SOUND_LANG1: 445118819Salex case VIDEO_SOUND_LANG2: 446118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3000); 447118819Salex break; 448118819Salex } 449118819Salex break; 450118819Salex case MSP_MODE_FM_SAT: 451118819Salex dprintk("msp3400: SAT setstereo: %s\n",strmode[mode]); 452118819Salex switch (mode) { 453118819Salex case VIDEO_SOUND_MONO: 454118819Salex msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); 455118819Salex break; 456118819Salex case VIDEO_SOUND_STEREO: 457118819Salex msp3400c_setcarrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); 458118819Salex break; 459118819Salex case VIDEO_SOUND_LANG1: 460118819Salex msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); 461118819Salex break; 462118819Salex case VIDEO_SOUND_LANG2: 463118819Salex msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); 464118819Salex break; 465118819Salex } 466118819Salex break; 467118819Salex case MSP_MODE_FM_NICAM1: 468118819Salex case MSP_MODE_FM_NICAM2: 469118819Salex case MSP_MODE_AM_NICAM: 470118819Salex dprintk("msp3400: NICAM setstereo: %s\n",strmode[mode]); 471118819Salex msp3400c_setcarrier(client,msp->second,msp->main); 472118819Salex if (msp->nicam_on) 473118819Salex nicam=0x0100; 474118819Salex break; 475118819Salex case MSP_MODE_BTSC: 476118819Salex dprintk("msp3400: BTSC setstereo: %s\n",strmode[mode]); 477118819Salex nicam=0x0300; 478118819Salex break; 479118819Salex case MSP_MODE_EXTERN: 480118819Salex dprintk("msp3400: extern setstereo: %s\n",strmode[mode]); 481118819Salex nicam = 0x0200; 482118819Salex break; 483118819Salex case MSP_MODE_FM_RADIO: 484118819Salex dprintk("msp3400: FM-Radio setstereo: %s\n",strmode[mode]); 485118819Salex break; 486118819Salex default: 487118819Salex dprintk("msp3400: mono setstereo\n"); 488118819Salex return; 489118819Salex } 490118819Salex 491118819Salex /* switch audio */ 492118819Salex switch (mode) { 493118819Salex case VIDEO_SOUND_STEREO: 494118819Salex src = 0x0020 | nicam; 495118819Salex#if 0 496118819Salex /* spatial effect */ 497118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0005,0x4000); 498118819Salex#endif 499118819Salex break; 500118819Salex case VIDEO_SOUND_MONO: 501118819Salex if (msp->mode == MSP_MODE_AM_NICAM) { 502118819Salex dprintk("msp3400: switching to AM mono\n"); 503118819Salex /* AM mono decoding is handled by tuner, not MSP chip */ 504118819Salex /* SCART switching control register */ 505118819Salex msp3400c_set_scart(client,SCART_MONO,0); 506118819Salex src = 0x0200; 507118819Salex break; 508118819Salex } 509118819Salex case VIDEO_SOUND_LANG1: 510118819Salex src = 0x0000 | nicam; 511118819Salex break; 512118819Salex case VIDEO_SOUND_LANG2: 513118819Salex src = 0x0010 | nicam; 514118819Salex break; 515118819Salex } 516118819Salex dprintk("msp3400: setstereo final source/matrix = 0x%x\n", src); 517118819Salex 518118819Salex if (client->dolby) { 519118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,0x0520); 520118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,0x0620); 521118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); 522118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); 523118819Salex } else { 524118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,src); 525118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,src); 526118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); 527118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); 528118819Salex } 529118819Salex} 530118819Salex 531118819Salexstatic void 532118819Salexmsp3400c_print_mode(struct msp3400c *msp) 533118819Salex{ 534118819Salex if (msp->main == msp->second) { 535118819Salex printf("bktr: msp3400: mono sound carrier: %d.%03d MHz\n", 536118819Salex msp->main/910000,(msp->main/910)%1000); 537118819Salex } else { 538118819Salex printf("bktr: msp3400: main sound carrier: %d.%03d MHz\n", 539118819Salex msp->main/910000,(msp->main/910)%1000); 540118819Salex } 541118819Salex if (msp->mode == MSP_MODE_FM_NICAM1 || 542118819Salex msp->mode == MSP_MODE_FM_NICAM2) 543118819Salex printf("bktr: msp3400: NICAM/FM carrier : %d.%03d MHz\n", 544118819Salex msp->second/910000,(msp->second/910)%1000); 545118819Salex if (msp->mode == MSP_MODE_AM_NICAM) 546118819Salex printf("bktr: msp3400: NICAM/AM carrier : %d.%03d MHz\n", 547118819Salex msp->second/910000,(msp->second/910)%1000); 548118819Salex if (msp->mode == MSP_MODE_FM_TERRA && 549118819Salex msp->main != msp->second) { 550118819Salex printf("bktr: msp3400: FM-stereo carrier : %d.%03d MHz\n", 551118819Salex msp->second/910000,(msp->second/910)%1000); 552118819Salex } 553118819Salex} 554118819Salex 555118819Salexstatic void 556118819Salexmsp3400c_restore_dfp(bktr_ptr_t client) 557118819Salex{ 558118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 559118819Salex int i; 560118819Salex 561118819Salex for (i = 0; i < DFP_COUNT; i++) { 562118819Salex if (-1 == msp->dfp_regs[i]) 563118819Salex continue; 564118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, i, msp->dfp_regs[i]); 565118819Salex } 566118819Salex} 567118819Salex 568118819Salex/* ----------------------------------------------------------------------- */ 569118819Salex 570118819Salexstruct REGISTER_DUMP { 571118819Salex int addr; 572118819Salex char *name; 573118819Salex}; 574118819Salex 575118819Salexstruct REGISTER_DUMP d1[] = { 576118819Salex { 0x007e, "autodetect" }, 577118819Salex { 0x0023, "C_AD_BITS " }, 578118819Salex { 0x0038, "ADD_BITS " }, 579118819Salex { 0x003e, "CIB_BITS " }, 580118819Salex { 0x0057, "ERROR_RATE" }, 581118819Salex}; 582118819Salex 583118819Salexstatic int 584118819Salexautodetect_stereo(bktr_ptr_t client) 585118819Salex{ 586118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 587118819Salex int val; 588118819Salex int newstereo = msp->stereo; 589118819Salex int newnicam = msp->nicam_on; 590118819Salex int update = 0; 591118819Salex 592118819Salex switch (msp->mode) { 593118819Salex case MSP_MODE_FM_TERRA: 594118819Salex val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18); 595118819Salex if (val > 32767) 596118819Salex val -= 65536; 597118819Salex dprintk("msp34xx: stereo detect register: %d\n",val); 598118819Salex 599118819Salex if (val > 4096) { 600118819Salex newstereo = VIDEO_SOUND_STEREO | VIDEO_SOUND_MONO; 601118819Salex } else if (val < -4096) { 602118819Salex newstereo = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; 603118819Salex } else { 604118819Salex newstereo = VIDEO_SOUND_MONO; 605118819Salex } 606118819Salex newnicam = 0; 607118819Salex break; 608118819Salex case MSP_MODE_FM_NICAM1: 609118819Salex case MSP_MODE_FM_NICAM2: 610118819Salex case MSP_MODE_AM_NICAM: 611118819Salex val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x23); 612118819Salex dprintk("msp34xx: nicam sync=%d, mode=%d\n",val & 1, (val & 0x1e) >> 1); 613118819Salex 614118819Salex if (val & 1) { 615118819Salex /* nicam synced */ 616118819Salex switch ((val & 0x1e) >> 1) { 617118819Salex case 0: 618118819Salex case 8: 619118819Salex newstereo = VIDEO_SOUND_STEREO; 620118819Salex break; 621118819Salex case 1: 622118819Salex case 9: 623118819Salex newstereo = VIDEO_SOUND_MONO 624118819Salex | VIDEO_SOUND_LANG1; 625118819Salex break; 626118819Salex case 2: 627118819Salex case 10: 628118819Salex newstereo = VIDEO_SOUND_MONO 629118819Salex | VIDEO_SOUND_LANG1 630118819Salex | VIDEO_SOUND_LANG2; 631118819Salex break; 632118819Salex default: 633118819Salex newstereo = VIDEO_SOUND_MONO; 634118819Salex break; 635118819Salex } 636118819Salex newnicam=1; 637118819Salex } else { 638118819Salex newnicam = 0; 639118819Salex newstereo = VIDEO_SOUND_MONO; 640118819Salex } 641118819Salex break; 642118819Salex case MSP_MODE_BTSC: 643118819Salex val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200); 644118819Salex dprintk("msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n", 645118819Salex val, 646118819Salex (val & 0x0002) ? "no" : "yes", 647118819Salex (val & 0x0004) ? "no" : "yes", 648118819Salex (val & 0x0040) ? "stereo" : "mono", 649118819Salex (val & 0x0080) ? ", nicam 2nd mono" : "", 650118819Salex (val & 0x0100) ? ", bilingual/SAP" : ""); 651118819Salex newstereo = VIDEO_SOUND_MONO; 652118819Salex if (val & 0x0040) newstereo |= VIDEO_SOUND_STEREO; 653118819Salex if (val & 0x0100) newstereo |= VIDEO_SOUND_LANG1; 654118819Salex break; 655118819Salex } 656118819Salex if (newstereo != msp->stereo) { 657118819Salex update = 1; 658118819Salex dprintk("msp34xx: watch: stereo %d => %d\n", 659118819Salex msp->stereo,newstereo); 660118819Salex msp->stereo = newstereo; 661118819Salex } 662118819Salex if (newnicam != msp->nicam_on) { 663118819Salex update = 1; 664118819Salex dprintk("msp34xx: watch: nicam %d => %d\n", 665118819Salex msp->nicam_on,newnicam); 666118819Salex msp->nicam_on = newnicam; 667118819Salex } 668118819Salex return update; 669118819Salex} 670118819Salex 671118819Salex/* 672118819Salex * A kernel thread for msp3400 control -- we don't want to block the 673118819Salex * in the ioctl while doing the sound carrier & stereo detect 674118819Salex */ 675118819Salex 676118819Salex/* stereo/multilang monitoring */ 677118819Salexstatic void watch_stereo(bktr_ptr_t client) 678118819Salex{ 679118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 680118819Salex 681118819Salex if (autodetect_stereo(client)) { 682118819Salex if (msp->stereo & VIDEO_SOUND_STEREO) 683118819Salex msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 684118819Salex else if (msp->stereo & VIDEO_SOUND_LANG1) 685118819Salex msp3400c_setstereo(client,VIDEO_SOUND_LANG1); 686118819Salex else 687118819Salex msp3400c_setstereo(client,VIDEO_SOUND_MONO); 688118819Salex } 689118819Salex if (client->stereo_once) 690118819Salex msp->watch_stereo = 0; 691118819Salex 692118819Salex} 693118819Salex 694118819Salexstatic void msp3400c_thread(void *data) 695118819Salex{ 696118819Salex bktr_ptr_t client = data; 697118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 698118819Salex 699118819Salex struct CARRIER_DETECT *cd; 700118819Salex int count, max1,max2,val1,val2, val,this; 701118819Salex 702118819Salex dprintk("msp3400: thread started\n"); 703118819Salex 704138935Sjulian mtx_lock(&Giant); 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); 895138935Sjulian mtx_unlock(&Giant); 896118819Salex 897172836Sjulian kproc_exit(0); 898118819Salex} 899118819Salex 900118819Salex/* ----------------------------------------------------------------------- */ 901118819Salex/* this one uses the automatic sound standard detection of newer */ 902118819Salex/* msp34xx chip versions */ 903118819Salex 904118819Salexstatic struct MODES { 905118819Salex int retval; 906118819Salex int main, second; 907118819Salex char *name; 908118819Salex} modelist[] = { 909118819Salex { 0x0000, 0, 0, "ERROR" }, 910118819Salex { 0x0001, 0, 0, "autodetect start" }, 911118819Salex { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" }, 912118819Salex { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" }, 913118819Salex { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" }, 914118819Salex { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" }, 915118819Salex { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" }, 916118819Salex { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" }, 917118819Salex { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" }, 918118819Salex { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" }, 919118819Salex { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" }, 920118819Salex { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" }, 921118819Salex { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" }, 922118819Salex { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" }, 923118819Salex { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" }, 924118819Salex { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" }, 925118819Salex { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" }, 926118819Salex { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" }, 927118819Salex { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, 928118819Salex { -1, 0, 0, NULL }, /* EOF */ 929118819Salex}; 930118819Salex 931118819Salexstatic void msp3410d_thread(void *data) 932118819Salex{ 933118819Salex bktr_ptr_t client = data; 934118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 935118819Salex int mode,val,i,std; 936118819Salex int timo = 0; 937118819Salex 938118819Salex dprintk("msp3410: thread started\n"); 939118819Salex 940138935Sjulian mtx_lock(&Giant); 941118819Salex for (;;) { 942118819Salex if (msp->rmmod) 943118819Salex goto done; 944118819Salex if (!msp->watch_stereo) 945118819Salex timo = 0; 946118819Salex else 947118819Salex timo = 10*hz; 948118819Salex tsleep(msp->kthread, PRIBIO, "idle", timo); 949118819Salex if (msp->rmmod) 950118819Salex goto done; 951118819Salex if (msp->halt_thread) { 952118819Salex msp->watch_stereo = 0; 953118819Salex msp->halt_thread = 0; 954118819Salex dprintk("msp3410: thread halted\n"); 955118819Salex continue; 956118819Salex } 957118819Salex 958118819Salex if (msp->mode == MSP_MODE_EXTERN) 959118819Salex continue; 960118819Salex 961118819Salex msp->active = 1; 962118819Salex 963118819Salex if (msp->watch_stereo) { 964118819Salex watch_stereo(client); 965118819Salex msp->active = 0; 966118819Salex continue; 967118819Salex } 968118819Salex 969118819Salex /* some time for the tuner to sync */ 970118819Salex tsleep(msp->kthread, PRIBIO, "tuner sync", hz/2); 971118819Salex 972118819Salex restart: 973118819Salex if (msp->mode == MSP_MODE_EXTERN) 974118819Salex continue; 975118819Salex msp->restart = 0; 976118819Salex msp->watch_stereo = 0; 977118819Salex 978118819Salex /* put into sane state (and mute) */ 979118819Salex msp3400c_reset(client); 980118819Salex 981118819Salex /* start autodetect */ 982118819Salex switch (msp->norm) { 983118819Salex case VIDEO_MODE_PAL: 984118819Salex mode = 0x1003; 985118819Salex std = 1; 986118819Salex break; 987118819Salex case VIDEO_MODE_NTSC: /* BTSC */ 988118819Salex mode = 0x2003; 989118819Salex std = 0x0020; 990118819Salex break; 991118819Salex case VIDEO_MODE_SECAM: 992118819Salex mode = 0x0003; 993118819Salex std = 1; 994118819Salex break; 995118819Salex case VIDEO_MODE_RADIO: 996118819Salex mode = 0x0003; 997118819Salex std = 0x0040; 998118819Salex break; 999118819Salex default: 1000118819Salex mode = 0x0003; 1001118819Salex std = 1; 1002118819Salex break; 1003118819Salex } 1004118819Salex msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode); 1005118819Salex msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std); 1006118819Salex 1007118819Salex if (bootverbose) { 1008118819Salex int i; 1009118819Salex for (i = 0; modelist[i].name != NULL; i++) 1010118819Salex if (modelist[i].retval == std) 1011118819Salex break; 1012118819Salex dprintk("msp3410: setting mode: %s (0x%04x)\n", 1013118819Salex modelist[i].name ? modelist[i].name : "unknown",std); 1014118819Salex } 1015118819Salex 1016118819Salex if (std != 1) { 1017118819Salex /* programmed some specific mode */ 1018118819Salex val = std; 1019118819Salex } else { 1020118819Salex /* triggered autodetect */ 1021118819Salex for (;;) { 1022118819Salex tsleep(msp->kthread, PRIBIO, "autodetection", hz/10); 1023118819Salex if (msp->restart) 1024118819Salex goto restart; 1025118819Salex 1026118819Salex /* check results */ 1027118819Salex val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); 1028118819Salex if (val < 0x07ff) 1029118819Salex break; 1030118819Salex dprintk("msp3410: detection still in progress\n"); 1031118819Salex } 1032118819Salex } 1033118819Salex for (i = 0; modelist[i].name != NULL; i++) 1034118819Salex if (modelist[i].retval == val) 1035118819Salex break; 1036118819Salex dprintk("msp3410: current mode: %s (0x%04x)\n", 1037118819Salex modelist[i].name ? modelist[i].name : "unknown", 1038118819Salex val); 1039118819Salex msp->main = modelist[i].main; 1040118819Salex msp->second = modelist[i].second; 1041118819Salex 1042118819Salex if (client->amsound && (msp->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) { 1043118819Salex /* autodetection has failed, let backup */ 1044118819Salex dprintk("msp3410: autodetection failed, switching to backup mode: %s (0x%04x)\n", 1045118819Salex modelist[8].name ? modelist[8].name : "unknown",val); 1046118819Salex val = 0x0009; 1047118819Salex msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val); 1048118819Salex } 1049118819Salex 1050118819Salex /* set various prescales */ 1051118819Salex msp3400c_write(client, I2C_MSP3400C_DFP, 0x0d, 0x1900); /* scart */ 1052118819Salex msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */ 1053118819Salex msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* nicam */ 1054118819Salex 1055118819Salex /* set stereo */ 1056118819Salex switch (val) { 1057118819Salex case 0x0008: /* B/G NICAM */ 1058118819Salex case 0x000a: /* I NICAM */ 1059118819Salex if (val == 0x0008) 1060118819Salex msp->mode = MSP_MODE_FM_NICAM1; 1061118819Salex else 1062118819Salex msp->mode = MSP_MODE_FM_NICAM2; 1063118819Salex /* just turn on stereo */ 1064118819Salex msp->stereo = VIDEO_SOUND_STEREO; 1065118819Salex msp->nicam_on = 1; 1066118819Salex msp->watch_stereo = 1; 1067118819Salex msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 1068118819Salex break; 1069118819Salex case 0x0009: 1070118819Salex msp->mode = MSP_MODE_AM_NICAM; 1071118819Salex msp->stereo = VIDEO_SOUND_MONO; 1072118819Salex msp->nicam_on = 1; 1073118819Salex msp3400c_setstereo(client,VIDEO_SOUND_MONO); 1074118819Salex msp->watch_stereo = 1; 1075118819Salex break; 1076118819Salex case 0x0020: /* BTSC */ 1077118819Salex /* just turn on stereo */ 1078118819Salex msp->mode = MSP_MODE_BTSC; 1079118819Salex msp->stereo = VIDEO_SOUND_STEREO; 1080118819Salex msp->nicam_on = 0; 1081118819Salex msp->watch_stereo = 1; 1082118819Salex msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 1083118819Salex break; 1084118819Salex case 0x0040: /* FM radio */ 1085118819Salex msp->mode = MSP_MODE_FM_RADIO; 1086118819Salex msp->stereo = VIDEO_SOUND_STEREO; 1087118819Salex msp->nicam_on = 0; 1088118819Salex msp->watch_stereo = 0; 1089118819Salex /* scart routing */ 1090118819Salex msp3400c_set_scart(client,SCART_IN2,0); 1091118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0220); 1092118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0220); 1093118819Salex msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0220); 1094118819Salex break; 1095118819Salex case 0x0003: 1096118819Salex msp->mode = MSP_MODE_FM_TERRA; 1097118819Salex msp->stereo = VIDEO_SOUND_STEREO; 1098118819Salex msp->nicam_on = 0; 1099118819Salex msp->watch_stereo = 1; 1100118819Salex msp3400c_setstereo(client,VIDEO_SOUND_STEREO); 1101118819Salex break; 1102118819Salex } 1103118819Salex 1104118819Salex if (msp->watch_stereo) 1105118819Salex watch_stereo(client); 1106118819Salex 1107118819Salex /* unmute + restore dfp registers */ 1108118819Salex msp3400c_setbass(client, msp->bass); 1109118819Salex msp3400c_settreble(client, msp->treble); 1110118819Salex msp3400c_setvolume(client, msp->muted, msp->left, msp->right); 1111118819Salex msp3400c_restore_dfp(client); 1112118819Salex 1113118819Salex msp->active = 0; 1114118819Salex } 1115118819Salex 1116118819Salexdone: 1117118819Salex dprintk("msp3410: thread: exit\n"); 1118118819Salex msp->active = 0; 1119118819Salex 1120118819Salex msp->kthread = NULL; 1121118819Salex wakeup(&msp->kthread); 1122138935Sjulian mtx_unlock(&Giant); 1123118819Salex 1124172836Sjulian kproc_exit(0); 1125118819Salex} 1126118819Salex 1127118819Salexint msp_attach(bktr_ptr_t bktr) 1128118819Salex{ 1129118819Salex struct msp3400c *msp; 1130118819Salex int rev1,rev2,i; 1131118819Salex int err; 1132118819Salex char buf[20]; 1133118819Salex 1134118819Salex msp = (struct msp3400c *) malloc(sizeof(struct msp3400c), M_DEVBUF, M_NOWAIT); 1135118819Salex if (msp == NULL) 1136118819Salex return ENOMEM; 1137118819Salex bktr->msp3400c_info = msp; 1138118819Salex 1139118819Salex memset(msp,0,sizeof(struct msp3400c)); 1140118819Salex msp->left = 65535; 1141118819Salex msp->right = 65535; 1142118819Salex msp->bass = 32768; 1143118819Salex msp->treble = 32768; 1144118819Salex msp->input = -1; 1145118819Salex 1146118819Salex for (i = 0; i < DFP_COUNT; i++) 1147118819Salex msp->dfp_regs[i] = -1; 1148118819Salex 1149118819Salex msp3400c_reset(bktr); 1150118819Salex 1151118819Salex rev1 = msp3400c_read(bktr, I2C_MSP3400C_DFP, 0x1e); 1152118819Salex if (-1 != rev1) 1153118819Salex rev2 = msp3400c_read(bktr, I2C_MSP3400C_DFP, 0x1f); 1154118819Salex if ((-1 == rev1) || (0 == rev1 && 0 == rev2)) { 1155118819Salex free(msp, M_DEVBUF); 1156118819Salex bktr->msp3400c_info = NULL; 1157118819Salex printf("%s: msp3400: error while reading chip version\n", bktr_name(bktr)); 1158118819Salex return ENXIO; 1159118819Salex } 1160118819Salex 1161118819Salex#if 0 1162118819Salex /* this will turn on a 1kHz beep - might be useful for debugging... */ 1163118819Salex msp3400c_write(bktr,I2C_MSP3400C_DFP, 0x0014, 0x1040); 1164118819Salex#endif 1165118819Salex 1166118819Salex sprintf(buf,"MSP34%02d%c-%c%d", 1167118819Salex (rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); 1168118819Salex msp->nicam = (((rev2>>8)&0xff) != 00) ? 1 : 0; 1169118819Salex 1170118819Salex if (bktr->mspsimple == -1) { 1171118819Salex /* default mode */ 1172118819Salex /* msp->simple = (((rev2>>8)&0xff) == 0) ? 0 : 1; */ 1173118819Salex msp->simple = ((rev1&0xff)+'@' > 'C'); 1174118819Salex } else { 1175118819Salex /* use kenv value */ 1176118819Salex msp->simple = bktr->mspsimple; 1177118819Salex } 1178118819Salex 1179118819Salex /* hello world :-) */ 1180118819Salex if (bootverbose) { 1181118819Salex printf("%s: msp34xx: init: chip=%s", bktr_name(bktr), buf); 1182118819Salex if (msp->nicam) 1183118819Salex printf(", has NICAM support"); 1184118819Salex printf("\n"); 1185118819Salex } 1186118819Salex 1187118819Salex /* startup control thread */ 1188172836Sjulian err = kproc_create(msp->simple ? msp3410d_thread : msp3400c_thread, 1189118819Salex bktr, &msp->kthread, (RFFDG | RFPROC), 0, 1190250088Spluknet "%s_msp34xx_thread", bktr->bktr_xname); 1191118819Salex if (err) { 1192172836Sjulian printf("%s: Error returned by kproc_create: %d", bktr_name(bktr), err); 1193118819Salex free(msp, M_DEVBUF); 1194118819Salex bktr->msp3400c_info = NULL; 1195118819Salex return ENXIO; 1196118819Salex } 1197118819Salex wakeup(msp->kthread); 1198118819Salex 1199118819Salex /* done */ 1200118819Salex return 0; 1201118819Salex} 1202118819Salex 1203118819Salexint msp_detach(bktr_ptr_t client) 1204118819Salex{ 1205118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 1206118819Salex 1207118819Salex /* shutdown control thread */ 1208118819Salex if (msp->kthread) 1209118819Salex { 1210118819Salex /* XXX mutex lock required */ 1211138935Sjulian mtx_lock(&Giant); 1212118819Salex msp->rmmod = 1; 1213118819Salex msp->watch_stereo = 0; 1214118819Salex wakeup(msp->kthread); 1215118819Salex 1216118819Salex while (msp->kthread) 1217118819Salex tsleep(&msp->kthread, PRIBIO, "wait for kthread", hz/10); 1218138935Sjulian mtx_unlock(&Giant); 1219118819Salex } 1220118819Salex 1221118819Salex if (client->msp3400c_info != NULL) { 1222118819Salex free(client->msp3400c_info, M_DEVBUF); 1223118819Salex client->msp3400c_info = NULL; 1224118819Salex } 1225118819Salex 1226118819Salex msp3400c_reset(client); 1227118819Salex 1228118819Salex return 0; 1229118819Salex} 1230118819Salex 1231118819Salexvoid msp_wake_thread(bktr_ptr_t client) 1232118819Salex{ 1233118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 1234118819Salex 1235118819Salex msp3400c_setvolume(client,msp->muted,0,0); 1236118819Salex msp->watch_stereo=0; 1237118819Salex if (msp->active) 1238118819Salex msp->restart = 1; 1239118819Salex wakeup(msp->kthread); 1240118819Salex} 1241118819Salex 1242118819Salexvoid msp_halt_thread(bktr_ptr_t client) 1243118819Salex{ 1244118819Salex struct msp3400c *msp = (struct msp3400c*)client->msp3400c_info; 1245118819Salex 1246118819Salex msp3400c_setvolume(client,msp->muted,0,0); 1247118819Salex if (msp->active) 1248118819Salex msp->restart = 1; 1249118819Salex msp->halt_thread = 1; 1250118819Salex wakeup(msp->kthread); 1251118819Salex} 1252118819Salex#endif /* BKTR_NEW_MSP34XX_DRIVER */ 1253