1706f2543Smrg#ifdef HAVE_XORG_CONFIG_H
2706f2543Smrg#include <xorg-config.h>
3706f2543Smrg#endif
4706f2543Smrg
5706f2543Smrg#include <string.h>
6706f2543Smrg#include <unistd.h>
7706f2543Smrg
8706f2543Smrg#include "xf86.h"
9706f2543Smrg#include "xf86i2c.h"
10706f2543Smrg#include "msp3430.h"
11706f2543Smrg#include "i2c_def.h"
12706f2543Smrg
13706f2543Smrg#define CONTROL         0x00
14706f2543Smrg#define WR_DEM          0x10
15706f2543Smrg#define RD_DEM          0x11
16706f2543Smrg#define WR_DSP          0x12
17706f2543Smrg#define RD_DSP          0x13
18706f2543Smrg
19706f2543Smrg
20706f2543Smrgvoid InitMSP34xxG(MSP3430Ptr m);
21706f2543Smrgvoid InitMSP34x5D(MSP3430Ptr m);
22706f2543Smrgvoid CheckModeMSP34x5D(MSP3430Ptr m);
23706f2543Smrgchar *MSP_getProductName (CARD16 product_id);
24706f2543Smrgvoid mpause(int milliseconds);
25706f2543Smrg
26706f2543Smrg#define __MSPDEBUG__	0
27706f2543Smrg
28706f2543Smrg#if __MSPDEBUG__ > 3
29706f2543Smrg
30706f2543Smrgvoid MSPBeep(MSP3430Ptr m, CARD8 freq);
31706f2543Smrg#define __MSPBEEP MSPBeep(m,0x14);
32706f2543Smrg
33706f2543Smrg#else
34706f2543Smrg
35706f2543Smrg#define __MSPBEEP
36706f2543Smrg#endif
37706f2543Smrg
38706f2543Smrgstatic void SetMSP3430Control(MSP3430Ptr m, CARD8 RegAddress, CARD8 RegValueHigh, CARD8 RegValueLow)
39706f2543Smrg{
40706f2543Smrg   I2CByte data[3];
41706f2543Smrg
42706f2543Smrg   data[0]=RegAddress;
43706f2543Smrg   data[1]=RegValueHigh;
44706f2543Smrg   data[2]=RegValueLow;
45706f2543Smrg
46706f2543Smrg   I2C_WriteRead(&(m->d),data,3,NULL,0);
47706f2543Smrg}
48706f2543Smrg
49706f2543Smrgstatic void SetMSP3430Data(MSP3430Ptr m, CARD8 RegAddress, CARD8 RegSubAddressHigh, CARD8 RegSubAddressLow,
50706f2543Smrg     CARD8 RegValueHigh, CARD8 RegValueLow)
51706f2543Smrg{
52706f2543Smrg   I2CByte data[5];
53706f2543Smrg#ifdef MSP_DEBUG
54706f2543Smrg   if(!m->registers_present[RegSubAddressLow]){
55706f2543Smrg   	xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_ERROR, "Attempt to access non-existent register in MSP34xxX: 0x%02x 0x%02x 0x%02x <- 0x%02x 0x%02x\n",
56706f2543Smrg		RegAddress, RegSubAddressHigh, RegSubAddressLow, RegValueHigh, RegValueLow);
57706f2543Smrg   	}
58706f2543Smrg#endif
59706f2543Smrg
60706f2543Smrg   data[0] = RegAddress;
61706f2543Smrg   data[1] = RegSubAddressHigh;
62706f2543Smrg   data[2] = RegSubAddressLow;
63706f2543Smrg   data[3] = RegValueHigh;
64706f2543Smrg   data[4] = RegValueLow;
65706f2543Smrg
66706f2543Smrg   I2C_WriteRead(&(m->d),data,5,NULL,0);
67706f2543Smrg}
68706f2543Smrg
69706f2543Smrgstatic void GetMSP3430Data(MSP3430Ptr m, CARD8 RegAddress, CARD8 RegSubAddressHigh, CARD8 RegSubAddressLow,
70706f2543Smrg            CARD8 *RegValueHigh, CARD8 *RegValueLow)
71706f2543Smrg{
72706f2543Smrg   I2CByte  send[3];
73706f2543Smrg   I2CByte  receive[2];
74706f2543Smrg
75706f2543Smrg   send[0] = RegAddress;
76706f2543Smrg   send[1] = RegSubAddressHigh;
77706f2543Smrg   send[2] = RegSubAddressLow;
78706f2543Smrg
79706f2543Smrg   I2C_WriteRead(&(m->d), send, 3, receive, 2);
80706f2543Smrg
81706f2543Smrg   *RegValueHigh = receive[0];
82706f2543Smrg   *RegValueLow = receive[1];
83706f2543Smrg}
84706f2543Smrg
85706f2543Smrg#if __MSPDEBUG__ > 2
86706f2543Smrgstatic void MSP3430DumpStatus(MSP3430Ptr m)
87706f2543Smrg{
88706f2543SmrgCARD8 status_hi, status_lo;
89706f2543SmrgCARD8 subaddr, data[2];
90706f2543Smrg
91706f2543SmrgGetMSP3430Data(m, RD_DEM, 0x02, 0x00, &status_hi, &status_lo);
92706f2543Smrgxf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP34xx: SAP(8)=%d mono/NICAM(7)=%d stereo=%d %s O_1=%d O_0=%d 2nd car=%d 1st car=%d\n",
93706f2543Smrg		status_hi & 1, (status_lo>>7) & 1, (status_lo>>6)&1,
94706f2543Smrg		(status_lo>>5)? ( (status_hi>>1)&1? "bad NICAM reception" : "NICAM" ) :
95706f2543Smrg		                ((status_hi>>1)&1 ? "bogus" : "ANALOG FM/AM") ,
96706f2543Smrg		(status_lo>>4)&1, (status_lo>>3)&1,!( (status_lo>>2)&1), !((status_lo>>1)&1));
97706f2543Smrg
98706f2543SmrgGetMSP3430Data(m, RD_DEM, 0x00, 0x7E, &status_hi, &status_lo);
99706f2543Smrgxf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP34xx: standard result=0x%02x%02x\n",
100706f2543Smrg		status_hi, status_lo);
101706f2543Smrgsubaddr=0x0;
102706f2543SmrgI2C_WriteRead(&(m->d), &subaddr, 1, data, 2);
103706f2543Smrgxf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP34xx: control=0x%02x%02x\n",
104706f2543Smrg		data[1], data[0]);
105706f2543Smrg}
106706f2543Smrg#endif
107706f2543Smrg
108706f2543Smrg/* wrapper */
109706f2543Smrgvoid InitMSP3430(MSP3430Ptr m)
110706f2543Smrg{
111706f2543Smrg  #if __MSPDEBUG__ > 1
112706f2543Smrg  xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"InitMSP3430(m->connector=%d, m->standard=%d, m->chip_family=%d)\n",
113706f2543Smrg  		m->connector, m->standard, m->chip_family);
114706f2543Smrg  #endif
115706f2543Smrg	switch (m->chip_family) {
116706f2543Smrg		case MSPFAMILY_34x0G:
117706f2543Smrg			InitMSP34xxG(m);
118706f2543Smrg			break;
119706f2543Smrg		case MSPFAMILY_34x5G:
120706f2543Smrg			InitMSP34xxG(m);
121706f2543Smrg			break;
122706f2543Smrg		case MSPFAMILY_34x5D:
123706f2543Smrg			InitMSP34x5D(m);
124706f2543Smrg			break;
125706f2543Smrg	}
126706f2543Smrg}
127706f2543Smrg
128706f2543Smrg/*-----------------------------------------------------------------
129706f2543Smrg| common functions for all MSP34xx chips
130706f2543Smrg|----------------------------------------------------------------*/
131706f2543Smrg
132706f2543SmrgMSP3430Ptr DetectMSP3430(I2CBusPtr b, I2CSlaveAddr addr)
133706f2543Smrg{
134706f2543Smrg   MSP3430Ptr m;
135706f2543Smrg   I2CByte a;
136706f2543Smrg   CARD8 hardware_version, major_revision, product_code, rom_version;
137706f2543Smrg   Bool supported;
138706f2543Smrg
139706f2543Smrg   m = calloc(1,sizeof(MSP3430Rec));
140706f2543Smrg   if(m == NULL)return NULL;
141706f2543Smrg   m->d.DevName = strdup("MSP34xx");
142706f2543Smrg   m->d.SlaveAddr = addr;
143706f2543Smrg   m->d.pI2CBus = b;
144706f2543Smrg   m->d.NextDev = NULL;
145706f2543Smrg   m->d.StartTimeout = b->StartTimeout;
146706f2543Smrg   m->d.BitTimeout = b->BitTimeout;
147706f2543Smrg   m->d.AcknTimeout = b->AcknTimeout;
148706f2543Smrg   m->d.ByteTimeout = b->ByteTimeout;
149706f2543Smrg
150706f2543Smrg   if(!I2C_WriteRead(&(m->d), NULL, 0, &a, 1))
151706f2543Smrg   {
152706f2543Smrg       free(m->d.DevName);
153706f2543Smrg       free(m);
154706f2543Smrg	return NULL;
155706f2543Smrg    }
156706f2543Smrg
157706f2543Smrg
158706f2543Smrg	m->standard=MSP3430_NTSC;
159706f2543Smrg	m->connector=MSP3430_CONNECTOR_1;
160706f2543Smrg	m->mode=MSPMODE_STEREO_A;	/*stereo or chanel A if avail. */
161706f2543Smrg    	m->c_format=MSPFORMAT_UNKNOWN;
162706f2543Smrg    	m->c_standard=MSPSTANDARD_UNKNOWN;
163706f2543Smrg    	m->c_matrix=m->c_fmmatrix=m->c_source=0;
164706f2543Smrg	m->volume=0;
165706f2543Smrg	m->recheck=FALSE;
166706f2543Smrg
167706f2543Smrg   GetMSP3430Data(m, RD_DSP, 0x00, 0x1E, &hardware_version, &major_revision);
168706f2543Smrg   GetMSP3430Data(m, RD_DSP, 0x00, 0x1F, &product_code, &rom_version);
169706f2543Smrg   m->hardware_version=hardware_version;
170706f2543Smrg   m->major_revision=major_revision;
171706f2543Smrg   m->product_code=product_code;
172706f2543Smrg   m->rom_version=rom_version;
173706f2543Smrg
174706f2543Smrg   m->chip_id=((major_revision << 8) | product_code);
175706f2543Smrg
176706f2543Smrg   supported=FALSE;
177706f2543Smrg   switch (major_revision) {
178706f2543Smrg   case 4:	/* 34xxD */
179706f2543Smrg      switch (product_code) {
180706f2543Smrg	  case 0x05: /* 3405D */
181706f2543Smrg	  case 0x0A: /* 3410D */
182706f2543Smrg	  case 0x0F: /* 3415D */
183706f2543Smrg	  	m->chip_family=MSPFAMILY_34x5D;
184706f2543Smrg		m->recheck=TRUE;
185706f2543Smrg		supported=TRUE;
186706f2543Smrg		break;
187706f2543Smrg      default:
188706f2543Smrg	  	m->chip_family=MSPFAMILY_34x0D;
189706f2543Smrg      }
190706f2543Smrg	  break;
191706f2543Smrg   case 7:	/* 34xxG */
192706f2543Smrg   	switch(product_code){
193706f2543Smrg		case 0x00:
194706f2543Smrg		case 0x0A:
195706f2543Smrg		case 0x1E:
196706f2543Smrg		case 0x28:
197706f2543Smrg		case 0x32:
198706f2543Smrg		  	m->chip_family=MSPFAMILY_34x0G;
199706f2543Smrg			supported=TRUE;
200706f2543Smrg			break;
201706f2543Smrg		case 0x0f:
202706f2543Smrg		case 0x19:
203706f2543Smrg		case 0x2d:
204706f2543Smrg		case 0x37:
205706f2543Smrg		case 0x41:
206706f2543Smrg		  	m->chip_family=MSPFAMILY_34x5G;
207706f2543Smrg			supported=TRUE;
208706f2543Smrg			#ifdef MSP_DEBUG
209706f2543Smrg			memset(m->registers_present, 0, 256);
210706f2543Smrg			#define A(num) m->registers_present[(num)]=1;
211706f2543Smrg			#define B(num1, num2) memset(&(m->registers_present[num1]), 1, num2-num1);
212706f2543Smrg			A(0x20)
213706f2543Smrg			A(0x30)
214706f2543Smrg			A(0x40)
215706f2543Smrg			A(0x00)
216706f2543Smrg			B(0x01, 0x08)
217706f2543Smrg			B(0x0B, 0x0E)
218706f2543Smrg			A(0x10)
219706f2543Smrg			B(0x12,0x14)
220706f2543Smrg			A(0x16)
221706f2543Smrg			A(0x29)
222706f2543Smrg			#undef B
223706f2543Smrg			#undef A
224706f2543Smrg			#endif
225706f2543Smrg			break;
226706f2543Smrg		default:
227706f2543Smrg		  	m->chip_family=MSPFAMILY_UNKNOWN;
228706f2543Smrg		}
229706f2543Smrg		break;
230706f2543Smrg   default:
231706f2543Smrg	  	m->chip_family=MSPFAMILY_UNKNOWN;
232706f2543Smrg   }
233706f2543Smrg
234706f2543Smrg	xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "Found %s%s, rom version 0x%02x, chip_id=0x%04x\n",
235706f2543Smrg		MSP_getProductName(m->chip_id), supported?"":" (unsupported)", rom_version, m->chip_id);
236706f2543Smrg
237706f2543Smrg	if (!supported) {
238706f2543Smrg            free(m->d.DevName);
239706f2543Smrg            free(m);
240706f2543Smrg            return NULL;
241706f2543Smrg	}
242706f2543Smrg   if(!I2CDevInit(&(m->d)))
243706f2543Smrg   {
244706f2543Smrg       free(m->d.DevName);
245706f2543Smrg       free(m);
246706f2543Smrg       return NULL;
247706f2543Smrg   }
248706f2543Smrg
249706f2543Smrg   return m;
250706f2543Smrg}
251706f2543Smrg
252706f2543Smrgvoid ResetMSP3430(MSP3430Ptr m)
253706f2543Smrg{
254706f2543Smrg    /* Reset the MSP3430 */
255706f2543Smrg    SetMSP3430Control(m, 0x00, 0x80, 0x00);
256706f2543Smrg    /* Set it back to normal operation */
257706f2543Smrg    SetMSP3430Control(m, 0x00, 0x00, 0x00);
258706f2543Smrg
259706f2543Smrg    m->c_format=MSPFORMAT_UNKNOWN;
260706f2543Smrg    m->c_standard=MSPSTANDARD_UNKNOWN;
261706f2543Smrg    m->c_matrix=m->c_fmmatrix=m->c_source=0;
262706f2543Smrg	m->volume=0;
263706f2543Smrg}
264706f2543Smrg
265706f2543Smrgvoid MSP3430SetVolume (MSP3430Ptr m, CARD8 value)
266706f2543Smrg{
267706f2543Smrg    CARD8 result;
268706f2543Smrg#if 0
269706f2543Smrg    CARD8 old_volume;
270706f2543Smrg    GetMSP3430Data(m, RD_DSP, 0x00, 0x00, &old_volume, &result);
271706f2543Smrg    xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP3430 result 0x%02x\n", result);
272706f2543Smrg#endif
273706f2543Smrg    /* save an extra Get call */
274706f2543Smrg    result=0;
275706f2543Smrg
276706f2543Smrg    SetMSP3430Data(m, WR_DSP, 0x00, 0x00, value, result);
277706f2543Smrg
278706f2543Smrg    SetMSP3430Data(m, WR_DSP, 0x00, 0x07, value, 0);
279706f2543Smrg    m->volume=value;
280706f2543Smrg
281706f2543Smrg#if __MSPDEBUG__ > 2
282706f2543Smrg    MSP3430DumpStatus(m);
283706f2543Smrg    __MSPBEEP
284706f2543Smrg    GetMSP3430Data(m, RD_DSP, 0x00, 0x00, &old_volume, &result);
285706f2543Smrg    xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP3430 volume 0x%02x\n",value);
286706f2543Smrg#endif
287706f2543Smrg}
288706f2543Smrg
289706f2543Smrg
290706f2543Smrgvoid MSP3430SetSAP (MSP3430Ptr m, int mode)
291706f2543Smrg{
292706f2543Smrg	xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "Put actual code to change SAP here\n");
293706f2543Smrg
294706f2543Smrg      SetMSP3430Data(m, WR_DSP, 0x00, 0x08, mode & 0xff, 0x20);
295706f2543Smrg}
296706f2543Smrg
297706f2543Smrg
298706f2543Smrg#if 0
299706f2543Smrgvoid MSP3430SetSource(MSP3430Ptr m, CARD8 value)
300706f2543Smrg{
301706f2543Smrg    /* Write to DSP, register 0x0008, (loudspeaker channel source/matrix) */
302706f2543Smrg    /* This sets the source to the TV tuner, for stereo operation */
303706f2543Smrg    SetMSP3430Data(m, WR_DSP, 0x00, 0x08, value, 0x20);
304706f2543Smrg}
305706f2543Smrg#endif
306706f2543Smrg
307706f2543Smrg
308706f2543Smrgchar *MSP_getProductName (CARD16 product_id)
309706f2543Smrg{
310706f2543Smrg	switch (product_id) {
311706f2543Smrg		case 0x0400: return "MSP3400D";
312706f2543Smrg		case 0x040a: return "MSP3410D";
313706f2543Smrg		case 0x0405: return "MSP3405D";
314706f2543Smrg		case 0x040f: return "MSP3415D";
315706f2543Smrg		case 0x0700: return "MSP3400G";
316706f2543Smrg		case 0x070a: return "MSP3410G";
317706f2543Smrg		case 0x071e: return "MSP3430G";
318706f2543Smrg		case 0x0728: return "MSP3440G";
319706f2543Smrg		case 0x0732: return "MSP3450G";
320706f2543Smrg		case 0x070f: return "MSP3415G";
321706f2543Smrg		case 0x0719: return "MSP3425G";
322706f2543Smrg		case 0x072d: return "MSP3445G";
323706f2543Smrg		case 0x0737: return "MSP3455G";
324706f2543Smrg		case 0x0741: return "MSP3465G";
325706f2543Smrg	}
326706f2543Smrg	return "MSP - unknown type";
327706f2543Smrg}
328706f2543Smrg
329706f2543Smrg#if __MSPDEBUG__ > 2
330706f2543Smrg/*puts beep in MSP output
331706f2543Smrg    freq = 0x01 - 16Hz ... 0x40 - 1kHz ... 0xff - 4kHz
332706f2543Smrg*/
333706f2543Smrgvoid MSPBeep(MSP3430Ptr m, CARD8 freq) {
334706f2543Smrg    SetMSP3430Data (m, WR_DSP, 0x00, freq, 0x7f, 0x40);
335706f2543Smrg    mpause(100);
336706f2543Smrg    SetMSP3430Data (m, WR_DSP, 0x00, 0x14, 0x00, 0x00);
337706f2543Smrg}
338706f2543Smrg#endif
339706f2543Smrg
340706f2543Smrgvoid mpause(int milliseconds) {
341706f2543Smrg    int i,m;
342706f2543Smrg    m=milliseconds/20;
343706f2543Smrg    for (i=0;i<m;i++) usleep(20000);
344706f2543Smrg}
345706f2543Smrg
346706f2543Smrg/*-----------------------------------------------------------------
347706f2543Smrg| specific functions for all MSP34xxG chips
348706f2543Smrg|----------------------------------------------------------------*/
349706f2543Smrg
350706f2543Smrgvoid InitMSP34xxG(MSP3430Ptr m)
351706f2543Smrg{
352706f2543Smrg
353706f2543Smrg  #if __MSPDEBUG__ > 1
354706f2543Smrg  xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"InitMSP34xxG(m->connector=%d, m->standard=%d, m->chip_family=%d)\n",
355706f2543Smrg  		m->connector, m->standard, m->chip_family);
356706f2543Smrg  #endif
357706f2543Smrg   /* Reset MSP3430 */
358706f2543Smrg   SetMSP3430Control(m, 0x00, 0x80, 0x00);
359706f2543Smrg   /* Set it back to normal operation */
360706f2543Smrg   SetMSP3430Control(m, 0x00, 0x00, 0x00);
361706f2543Smrg
362706f2543Smrg   /*set MODUS register */
363706f2543Smrg   /* bits: 0 - automatic sound detection */
364706f2543Smrg   /*       1 - enable STATUS change */
365706f2543Smrg   /*       12 - detect 6.5 Mhz carrier as D/K1, D/K2 or D/K NICAM  (does not seem to work ) */
366706f2543Smrg   /*       13 - detect 4.5 Mhz carrier as BTSC */
367706f2543Smrg   if ( (m->standard & 0xff) == MSP3430_PAL )
368706f2543Smrg   {
369706f2543Smrg      SetMSP3430Data(m, WR_DEM, 0x00, 0x30, 0x30, 0x03|0x08);    /* make O_ pins tristate */
370706f2543Smrg      /* PAL standard */
371706f2543Smrg      SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x01); /* possibly wrong */
372706f2543Smrg   } else {
373706f2543Smrg      SetMSP3430Data(m, WR_DEM, 0x00, 0x30, 0x20, 0x03|0x08);
374706f2543Smrg      /* standard selection is M-BTSC-Stereo */
375706f2543Smrg      SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x20);
376706f2543Smrg   }
377706f2543Smrg
378706f2543Smrg   switch(m->connector){
379706f2543Smrg         case MSP3430_CONNECTOR_1:
380706f2543Smrg	        SetMSP3430Data(m, WR_DSP, 0x00, 0x08, 0x03, 0x20);
381706f2543Smrg	        break;
382706f2543Smrg	 case MSP3430_CONNECTOR_2:
383706f2543Smrg		/* this has not been checked yet.. could be bogus */
384706f2543Smrg    		/* SCART Input Prescale: 0 dB gain */
385706f2543Smrg		SetMSP3430Data(m, WR_DSP, 0x00, 0x0d, 0x19, 0x00);
386706f2543Smrg    		SetMSP3430Data(m, WR_DSP, 0x00, 0x08, 0x02, 0x20);
387706f2543Smrg		break;
388706f2543Smrg	case MSP3430_CONNECTOR_3:
389706f2543Smrg	default:
390706f2543Smrg    		/* SCART Input Prescale: 0 dB gain */
391706f2543Smrg		SetMSP3430Data(m, WR_DSP, 0x00, 0x0d, 0x19, 0x00);
392706f2543Smrg
393706f2543Smrg    		SetMSP3430Data(m, WR_DSP, 0x00, 0x08, 0x02, 0x20);
394706f2543Smrg    		break;
395706f2543Smrg	}
396706f2543Smrg
397706f2543Smrg    switch(m->standard){
398706f2543Smrg        case MSP3430_PAL:
399706f2543Smrg     		SetMSP3430Data(m, WR_DSP, 0x00, 0x0e, 0x24, 0x03);
400706f2543Smrg     		SetMSP3430Data(m, WR_DSP, 0x00, 0x10, 0x00, 0x5a);
401706f2543Smrg                SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x03);
402706f2543Smrg                /* Set volume to FAST_MUTE. */
403706f2543Smrg     	        SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);
404706f2543Smrg	        break;
405706f2543Smrg        case MSP3430_PAL_DK1:
406706f2543Smrg     		SetMSP3430Data(m, WR_DSP, 0x00, 0x0e, 0x24, 0x03);
407706f2543Smrg     		SetMSP3430Data(m, WR_DSP, 0x00, 0x10, 0x00, 0x5a);
408706f2543Smrg		SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x04);
409706f2543Smrg                /* Set volume to FAST_MUTE. */
410706f2543Smrg     	        SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);
411706f2543Smrg		break;
412706f2543Smrg	case MSP3430_SECAM: /* is this right ? */
413706f2543Smrg        case MSP3430_NTSC:
414706f2543Smrg                /* Write to DSP, register 0x000E, (prescale FM/FM matrix) */
415706f2543Smrg                SetMSP3430Data(m, WR_DSP, 0x00, 0x0e, 0x24, 0x03);
416706f2543Smrg
417706f2543Smrg                /* Set volume to FAST_MUTE. */
418706f2543Smrg                SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);
419706f2543Smrg		break;
420706f2543Smrg	}
421706f2543Smrg
422706f2543Smrg}
423706f2543Smrg
424706f2543Smrg/*-----------------------------------------------------------------
425706f2543Smrg| specific functions for all MSP34x5D chips
426706f2543Smrg|----------------------------------------------------------------*/
427706f2543Smrg
428706f2543Smrgvoid InitMSP34x5D(MSP3430Ptr m)
429706f2543Smrg{
430706f2543Smrgint count;
431706f2543SmrgCARD8 high,low;
432706f2543SmrgCARD16 result,standard;
433706f2543SmrgCARD16 peak;
434706f2543Smrg
435706f2543Smrg
436706f2543Smrgif (m->c_format==MSPFORMAT_UNKNOWN) ResetMSP3430(m);
437706f2543Smrgelse {
438706f2543Smrg    /*mute volume*/
439706f2543Smrg    SetMSP3430Data (m, WR_DSP, 0x00, 0x00, 0x00, 0x00);
440706f2543Smrg}
441706f2543Smrg
442706f2543Smrg
443706f2543Smrg
444706f2543Smrg    switch(m->connector){
445706f2543Smrg	case MSP3430_CONNECTOR_2:
446706f2543Smrg	case MSP3430_CONNECTOR_3:
447706f2543Smrg	    if (m->c_format!=MSPFORMAT_SCART) {
448706f2543Smrg    		/* SCART Input Prescale: 0 dB gain */
449706f2543Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x0d, 0x19, 0x00);
450706f2543Smrg			/* this has not been checked yet.. could be bogus */
451706f2543Smrg			m->c_format=MSPFORMAT_SCART;	/*stereo*/
452706f2543Smrg		}
453706f2543Smrg	    break;
454706f2543Smrg	case MSP3430_CONNECTOR_1:
455706f2543Smrg	default:
456706f2543Smrg
457706f2543Smrg	    switch ( m->standard & 0x00ff ) {
458706f2543Smrg		case MSP3430_PAL:
459706f2543Smrg			switch( m->standard ) {
460706f2543Smrg			case MSP3430_PAL_DK1:
461706f2543Smrg			    standard=MSPSTANDARD_FM_DK1;
462706f2543Smrg			    break;
463706f2543Smrg/*			case MSP3430_PAL_DK2:
464706f2543Smrg			    standard=MSPSTANDARD_FM_DK2;
465706f2543Smrg			    break;
466706f2543Smrg			case MSP3430_PAL_BG:
467706f2543Smrg			may be FM stereo (Germany) or FM NICAM (Scandinavia,spain)
468706f2543Smrg			    standard=MSPSTANDARD_AUTO;
469706f2543Smrg			    break;
470706f2543Smrg*/
471706f2543Smrg			default:
472706f2543Smrg			    standard=MSPSTANDARD_AUTO;
473706f2543Smrg			}
474706f2543Smrg		    break;
475706f2543Smrg		case MSP3430_SECAM:
476706f2543Smrg		    standard=MSPSTANDARD_AUTO;
477706f2543Smrg		case MSP3430_NTSC:
478706f2543Smrg			    /* Only MSP34x5 supported format - Korean NTSC-M*/
479706f2543Smrg			 standard=MSPSTANDARD_FM_M;
480706f2543Smrg		default:
481706f2543Smrg		    standard=MSPSTANDARD_AUTO;
482706f2543Smrg		}
483706f2543Smrg
484706f2543Smrg	    /*no NICAM support in MSP3410D - force to autodetect*/
485706f2543Smrg	    if ((m->chip_id==0x405) && (standard>=MSPSTANDARD_NICAM_BG))
486706f2543Smrg    		standard=MSPSTANDARD_AUTO;
487706f2543Smrg
488706f2543Smrg	    if (m->c_standard != standard) {
489706f2543Smrg
490706f2543Smrg   	        SetMSP3430Data (m, WR_DEM, 0x00, 0x20, standard>>8, standard & 0xFF);
491706f2543Smrg	        if (standard==MSPSTANDARD_AUTO) {
492706f2543Smrg			    count = 50; /* time shouldn't exceed 1s, just in case */
493706f2543Smrg			    do {
494706f2543Smrg     		        usleep(20000);
495706f2543Smrg     		        GetMSP3430Data (m, RD_DEM, 0x00, 0x7e, &high, &low);
496706f2543Smrg			        result = ( high << 8 ) | low;
497706f2543Smrg     		        --count;
498706f2543Smrg	    	    } while( result > 0x07ff && count > 0 );
499706f2543Smrg
500706f2543Smrg		    	if ((result > MSPSTANDARD_AUTO))
501706f2543Smrg					standard=result;
502706f2543Smrg		    	else standard=MSPSTANDARD_UNKNOWN;
503706f2543Smrg#if __MSPDEBUG__ > 1
504706f2543Smrg				xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Detected audio standard: %d\n",result);
505706f2543Smrg#endif
506706f2543Smrg		    	/* result = MSPSTANDARD_NICAM_L can be one of:
507706f2543Smrg		    	SECAM_L - MSPSTANDARD_NICAM_L
508706f2543Smrg		    	D/K1 - MSPSTANDARD_FM_DK1
509706f2543Smrg		    	D/K2 - MSPSTANDARD_FM_DK2
510706f2543Smrg		    	D/K-NICAM - MSPSTANDARD_NICAM_DK*/
511706f2543Smrg		    	if( standard == MSPSTANDARD_NICAM_L ) {
512706f2543Smrg		        	if ((m->standard & 0x00ff)==MSP3430_PAL) {
513706f2543Smrg			    		/* force PAL D/K  */
514706f2543Smrg			    		standard=MSPSTANDARD_FM_DK1;
515706f2543Smrg		            	SetMSP3430Data (m, WR_DEM, 0x00, 0x20, standard>>8, standard & 0xFF);
516706f2543Smrg#if __MSPDEBUG__ > 1
517706f2543Smrg			        	xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO, "Detected 6.5MHz carrier - forced to D/K1 !!!\n" );
518706f2543Smrg#endif
519706f2543Smrg					}
520706f2543Smrg		    	}
521706f2543Smrg			}
522706f2543Smrg			m->c_standard=standard;
523706f2543Smrg	    } /*end - standard changed*/
524706f2543Smrg	    else {
525706f2543Smrg			if (standard<MSPSTANDARD_NICAM_BG) {
526706f2543Smrg    		    /* get old value of ident. mode register*/
527706f2543Smrg		    	GetMSP3430Data (m, RD_DSP, 0x00, 0x15, &high, &low);
528706f2543Smrg    		    /* reset Ident-Filter */
529706f2543Smrg    		    SetMSP3430Data (m, WR_DSP, 0x00, 0x14, 0x00, 0x3F);
530706f2543Smrg		    	/* put back old value to ident. mode register*/
531706f2543Smrg    		    SetMSP3430Data (m, WR_DSP, 0x00, 0x14, 0x00, low);
532706f2543Smrg			}
533706f2543Smrg	    }
534706f2543Smrg
535706f2543Smrg	    if (standard<=MSPSTANDARD_AUTO) {
536706f2543Smrg    	   	m->c_format=MSPFORMAT_1xFM;
537706f2543Smrg	    }
538706f2543Smrg	    else if (standard<MSPSTANDARD_NICAM_BG) {
539706f2543Smrg			/* set FM prescale */
540706f2543Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, 0x30, 0);
541706f2543Smrg			/* set FM deemphasis*/
542706f2543Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x0f, ((standard==MSPSTANDARD_FM_M)?0:1), 0);
543706f2543Smrg
544706f2543Smrg			/* check if FM2 carrier is present */
545706f2543Smrg			/*turn off FM DC Notch*/
546706f2543Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x17, 0x00, 0x3f);
547706f2543Smrg			/*matrix source for Quasi-Peak Detector - stereo: ch2->L ch1->R*/
548706f2543Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x0c, 0x00, 0x20);
549706f2543Smrg
550706f2543Smrg			mpause(250);
551706f2543Smrg   		    GetMSP3430Data (m, RD_DSP, 0x00, 0x1A, &high, &low);
552706f2543Smrg			peak = (high << 8) | low;
553706f2543Smrg#if __MSPDEBUG__ > 1
554706f2543Smrg			xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Second carrier Quasi-Peak detection: %d\n",peak);
555706f2543Smrg#endif
556706f2543Smrg			/*turn on FM DC Notch*/
557706f2543Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x17, 0x00, 0x00);
558706f2543Smrg
559706f2543Smrg			if (peak<5) {
560706f2543Smrg	    	    /* if second carrier not detected - only mono from first carrier*/
561706f2543Smrg	    	    m->c_format=MSPFORMAT_1xFM;
562706f2543Smrg			}
563706f2543Smrg			else {
564706f2543Smrg	    	    m->c_format=MSPFORMAT_2xFM;
565706f2543Smrg    	   	    /*start of FM identification process - FM_WAIT
566706f2543Smrg	    	    wait at least 0.5s - used 1s - gives beter resolution*/
567706f2543Smrg    	   	    mpause(1000);
568706f2543Smrg			}
569706f2543Smrg	    }
570706f2543Smrg	    else {
571706f2543Smrg			if (standard==MSPSTANDARD_NICAM_L) {
572706f2543Smrg	    	    m->c_format=MSPFORMAT_NICAM_AM;
573706f2543Smrg		    	/* set AM prescale */
574706f2543Smrg		    	SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, 0x7C, 0);
575706f2543Smrg			}
576706f2543Smrg			else {
577706f2543Smrg	    	    m->c_format=MSPFORMAT_NICAM_FM;
578706f2543Smrg		    	/* set FM prescale */
579706f2543Smrg			    SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, 0x30, 0);
580706f2543Smrg			}
581706f2543Smrg			/* set FM deemphasis*/
582706f2543Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x0f, 0x00, 0);
583706f2543Smrg			/* set NICAM prescale to 0dB */
584706f2543Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x10, 0x20, 0);
585706f2543Smrg	    }
586706f2543Smrg
587706f2543Smrg	    break;
588706f2543Smrg	} /*end - case conector*/
589706f2543Smrg
590706f2543Smrg    CheckModeMSP34x5D(m);
591706f2543Smrg
592706f2543Smrg    /* Set volume to FAST_MUTE. */
593706f2543Smrg    /*SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);*/
594706f2543Smrg    /*set volume*/
595706f2543Smrg	MSP3430SetVolume(m,m->volume);
596706f2543Smrg
597706f2543Smrg    __MSPBEEP
598706f2543Smrg
599706f2543Smrg
600706f2543Smrg} /* EnableMSP34x5D ()... */
601706f2543Smrg
602706f2543Smrg
603706f2543Smrg
604706f2543Smrg
605706f2543Smrgvoid CheckModeMSP34x5D(MSP3430Ptr m) {
606706f2543Smrg    const char stereo_on=25;
607706f2543Smrg    const char stereo_off=20;
608706f2543Smrg    const char dual_on=-stereo_on;
609706f2543Smrg    const char dual_off=-stereo_off;
610706f2543Smrg    char detect;
611706f2543Smrg    CARD8 matrix, fmmatrix, source, high, low;
612706f2543Smrg
613706f2543Smrg    fmmatrix=0;		/*no matrix*/
614706f2543Smrg    source=0;		/*FM*/
615706f2543Smrg    switch (m->c_format) {
616706f2543Smrg	case MSPFORMAT_NICAM_FM:
617706f2543Smrg	case MSPFORMAT_NICAM_AM:
618706f2543Smrg	case MSPFORMAT_SCART:
619706f2543Smrg	    source=( (m->c_format == MSPFORMAT_SCART)?2:1 );
620706f2543Smrg	    switch (m->mode) {
621706f2543Smrg		case MSPMODE_MONO:
622706f2543Smrg		    matrix=0x30;	/*MONO*/
623706f2543Smrg		    break;
624706f2543Smrg		case MSPMODE_A:
625706f2543Smrg		    matrix=0x00;	/*A*/
626706f2543Smrg		    break;
627706f2543Smrg		case MSPMODE_B:
628706f2543Smrg		    matrix=0x10;	/*B*/
629706f2543Smrg		    break;
630706f2543Smrg		default:
631706f2543Smrg		    matrix=0x20;	/*STEREO*/
632706f2543Smrg		    break;
633706f2543Smrg		}
634706f2543Smrg	    break;
635706f2543Smrg	default:
636706f2543Smrg	case MSPFORMAT_1xFM:
637706f2543Smrg	    matrix=0x00;	/*A*/
638706f2543Smrg	    break;
639706f2543Smrg	case MSPFORMAT_2xFM:
640706f2543Smrg	    switch (m->mode) {
641706f2543Smrg		case MSPMODE_MONO:
642706f2543Smrg		    matrix=0x30;	/*MONO*/
643706f2543Smrg		    break;
644706f2543Smrg		case MSPMODE_STEREO:
645706f2543Smrg		    matrix=0x20;	/*STEREO*/
646706f2543Smrg		    fmmatrix=((m->c_standard==MSPSTANDARD_FM_M)?2:1);
647706f2543Smrg		    break;
648706f2543Smrg		case MSPMODE_AB:
649706f2543Smrg		    matrix=0x20;	/*STEREO*/
650706f2543Smrg		    break;
651706f2543Smrg		case MSPMODE_A:
652706f2543Smrg		    matrix=0x00;	/*A*/
653706f2543Smrg		    break;
654706f2543Smrg		case MSPMODE_B:
655706f2543Smrg		    matrix=0x10;	/*B*/
656706f2543Smrg		    break;
657706f2543Smrg		default:
658706f2543Smrg    		/*FM_IDENT_CHECK*/
659706f2543Smrg    		GetMSP3430Data (m, RD_DSP, 0x00, 0x18, &high, &low);
660706f2543Smrg    		detect=(char)high;
661706f2543Smrg#if __MSPDEBUG__ > 1
662706f2543Smrg    		xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Stereo Detection Register: %d\n",detect);
663706f2543Smrg#endif
664706f2543Smrg    		if (detect>=((m->c_mode==MSPMODE_STEREO)?stereo_off:stereo_on)) {
665706f2543Smrg				m->c_mode=MSPMODE_STEREO;
666706f2543Smrg				matrix=0x20;	/*STEREO*/
667706f2543Smrg				fmmatrix=((m->c_standard==MSPSTANDARD_FM_M)?2:1);
668706f2543Smrg		    }
669706f2543Smrg			else if (detect<=((m->c_mode==MSPMODE_AB)?dual_off:dual_on)) {
670706f2543Smrg				m->c_mode=MSPMODE_AB;
671706f2543Smrg    			switch (m->mode) {
672706f2543Smrg			    case MSPMODE_STEREO_AB: matrix=0x20; break;
673706f2543Smrg			    case MSPMODE_STEREO_B: matrix=0x10; break;
674706f2543Smrg			    default:
675706f2543Smrg				case MSPMODE_A: matrix=0x00; break;
676706f2543Smrg				}
677706f2543Smrg			}
678706f2543Smrg    		else {
679706f2543Smrg				m->c_mode=MSPMODE_MONO;
680706f2543Smrg				matrix=0x30;	/*MONO*/
681706f2543Smrg			}
682706f2543Smrg		    break;
683706f2543Smrg	    } /* end - case mode*/
684706f2543Smrg	    break;
685706f2543Smrg    }
686706f2543Smrg
687706f2543Smrg    if (m->c_fmmatrix != fmmatrix) {
688706f2543Smrg        GetMSP3430Data (m, RD_DSP, 0x00, 0x0e, &high, &low);
689706f2543Smrg		SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, high, fmmatrix);
690706f2543Smrg		m->c_fmmatrix = fmmatrix;
691706f2543Smrg    }
692706f2543Smrg
693706f2543Smrg    if ((m->c_matrix != matrix) || (m->c_source != source)) {
694706f2543Smrg        /*set chanel source and matrix for loudspeaker*/
695706f2543Smrg		SetMSP3430Data (m, WR_DSP, 0x00, 0x08, source, matrix);
696706f2543Smrg
697706f2543Smrg		m->c_matrix = matrix;
698706f2543Smrg		m->c_source = source;
699706f2543Smrg    }
700706f2543Smrg
701706f2543Smrg	if ( ((m->c_format) & 0xF0) == MSPFORMAT_NICAM)
702706f2543Smrg			SetMSP3430Data (m, WR_DEM, 0x00, 0x21, 0, 1);
703706f2543Smrg
704706f2543Smrg#if __MSPDEBUG__ > 0
705706f2543Smrg		    char *msg;
706706f2543Smrg		    switch (matrix) {
707706f2543Smrg			case 0x30: /*MONO*/
708706f2543Smrg			    msg="MONO";
709706f2543Smrg			    break;
710706f2543Smrg			case 0x00: /*LEFT*/
711706f2543Smrg			    msg="MONO/CHANNEL_1";
712706f2543Smrg			    break;
713706f2543Smrg			case 0x10: /*RIGHT*/
714706f2543Smrg			    msg="MONO/CHANNEL_2";
715706f2543Smrg			    break;
716706f2543Smrg			case 0x20: /*LEFT*/
717706f2543Smrg			    msg="STEREO";
718706f2543Smrg			    break;
719706f2543Smrg			default:
720706f2543Smrg			    msg="unknown";
721706f2543Smrg			    break;
722706f2543Smrg		    }
723706f2543Smrg    		    xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Audio mode set to: %s\n",msg);
724706f2543Smrg#endif
725706f2543Smrg}
726706f2543Smrg
727