1016a45b2Smrg#ifdef HAVE_XORG_CONFIG_H
2016a45b2Smrg#include <xorg-config.h>
3016a45b2Smrg#endif
4016a45b2Smrg
5016a45b2Smrg#include <string.h>
6016a45b2Smrg#include <unistd.h>
7016a45b2Smrg
8016a45b2Smrg#include "xf86.h"
9016a45b2Smrg#include "xf86i2c.h"
10016a45b2Smrg#include "msp3430.h"
11016a45b2Smrg#include "i2c_def.h"
12016a45b2Smrg
13016a45b2Smrg#define CONTROL         0x00
14016a45b2Smrg#define WR_DEM          0x10
15016a45b2Smrg#define RD_DEM          0x11
16016a45b2Smrg#define WR_DSP          0x12
17016a45b2Smrg#define RD_DSP          0x13
18016a45b2Smrg
19016a45b2Smrg
20016a45b2Smrgvoid InitMSP34xxG(MSP3430Ptr m);
21016a45b2Smrgvoid InitMSP34x5D(MSP3430Ptr m);
22016a45b2Smrgvoid CheckModeMSP34x5D(MSP3430Ptr m);
23016a45b2Smrgchar *MSP_getProductName (CARD16 product_id);
24016a45b2Smrgvoid mpause(int milliseconds);
25016a45b2Smrg
26016a45b2Smrg#define __MSPDEBUG__	0
27016a45b2Smrg
28016a45b2Smrg#if __MSPDEBUG__ > 3
29016a45b2Smrg
30016a45b2Smrgvoid MSPBeep(MSP3430Ptr m, CARD8 freq);
31016a45b2Smrg#define __MSPBEEP MSPBeep(m,0x14);
32016a45b2Smrg
33016a45b2Smrg#else
34016a45b2Smrg
35016a45b2Smrg#define __MSPBEEP
36016a45b2Smrg#endif
37016a45b2Smrg
38016a45b2Smrgstatic void SetMSP3430Control(MSP3430Ptr m, CARD8 RegAddress, CARD8 RegValueHigh, CARD8 RegValueLow)
39016a45b2Smrg{
40016a45b2Smrg   I2CByte data[3];
41016a45b2Smrg
42016a45b2Smrg   data[0]=RegAddress;
43016a45b2Smrg   data[1]=RegValueHigh;
44016a45b2Smrg   data[2]=RegValueLow;
45016a45b2Smrg
46016a45b2Smrg   I2C_WriteRead(&(m->d),data,3,NULL,0);
47016a45b2Smrg}
48016a45b2Smrg
49016a45b2Smrgstatic void SetMSP3430Data(MSP3430Ptr m, CARD8 RegAddress, CARD8 RegSubAddressHigh, CARD8 RegSubAddressLow,
50016a45b2Smrg     CARD8 RegValueHigh, CARD8 RegValueLow)
51016a45b2Smrg{
52016a45b2Smrg   I2CByte data[5];
53016a45b2Smrg#ifdef MSP_DEBUG
54016a45b2Smrg   if(!m->registers_present[RegSubAddressLow]){
55016a45b2Smrg   	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",
56016a45b2Smrg		RegAddress, RegSubAddressHigh, RegSubAddressLow, RegValueHigh, RegValueLow);
57016a45b2Smrg   	}
58016a45b2Smrg#endif
59016a45b2Smrg
60016a45b2Smrg   data[0] = RegAddress;
61016a45b2Smrg   data[1] = RegSubAddressHigh;
62016a45b2Smrg   data[2] = RegSubAddressLow;
63016a45b2Smrg   data[3] = RegValueHigh;
64016a45b2Smrg   data[4] = RegValueLow;
65016a45b2Smrg
66016a45b2Smrg   I2C_WriteRead(&(m->d),data,5,NULL,0);
67016a45b2Smrg}
68016a45b2Smrg
69016a45b2Smrgstatic void GetMSP3430Data(MSP3430Ptr m, CARD8 RegAddress, CARD8 RegSubAddressHigh, CARD8 RegSubAddressLow,
70016a45b2Smrg            CARD8 *RegValueHigh, CARD8 *RegValueLow)
71016a45b2Smrg{
72016a45b2Smrg   I2CByte  send[3];
73016a45b2Smrg   I2CByte  receive[2];
74016a45b2Smrg
75016a45b2Smrg   send[0] = RegAddress;
76016a45b2Smrg   send[1] = RegSubAddressHigh;
77016a45b2Smrg   send[2] = RegSubAddressLow;
78016a45b2Smrg
79016a45b2Smrg   I2C_WriteRead(&(m->d), send, 3, receive, 2);
80016a45b2Smrg
81016a45b2Smrg   *RegValueHigh = receive[0];
82016a45b2Smrg   *RegValueLow = receive[1];
83016a45b2Smrg}
84016a45b2Smrg
85016a45b2Smrg#if __MSPDEBUG__ > 2
86016a45b2Smrgstatic void MSP3430DumpStatus(MSP3430Ptr m)
87016a45b2Smrg{
88016a45b2SmrgCARD8 status_hi, status_lo;
89016a45b2SmrgCARD8 subaddr, data[2];
90016a45b2Smrg
91016a45b2SmrgGetMSP3430Data(m, RD_DEM, 0x02, 0x00, &status_hi, &status_lo);
92016a45b2Smrgxf86DrvMsg(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",
93016a45b2Smrg		status_hi & 1, (status_lo>>7) & 1, (status_lo>>6)&1,
94016a45b2Smrg		(status_lo>>5)? ( (status_hi>>1)&1? "bad NICAM reception" : "NICAM" ) :
95016a45b2Smrg		                ((status_hi>>1)&1 ? "bogus" : "ANALOG FM/AM") ,
96016a45b2Smrg		(status_lo>>4)&1, (status_lo>>3)&1,!( (status_lo>>2)&1), !((status_lo>>1)&1));
97016a45b2Smrg
98016a45b2SmrgGetMSP3430Data(m, RD_DEM, 0x00, 0x7E, &status_hi, &status_lo);
99016a45b2Smrgxf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP34xx: standard result=0x%02x%02x\n",
100016a45b2Smrg		status_hi, status_lo);
101016a45b2Smrgsubaddr=0x0;
102016a45b2SmrgI2C_WriteRead(&(m->d), &subaddr, 1, data, 2);
103016a45b2Smrgxf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP34xx: control=0x%02x%02x\n",
104016a45b2Smrg		data[1], data[0]);
105016a45b2Smrg}
106016a45b2Smrg#endif
107016a45b2Smrg
108016a45b2Smrg/* wrapper */
109016a45b2Smrgvoid InitMSP3430(MSP3430Ptr m)
110016a45b2Smrg{
111016a45b2Smrg  #if __MSPDEBUG__ > 1
112016a45b2Smrg  xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"InitMSP3430(m->connector=%d, m->standard=%d, m->chip_family=%d)\n",
113016a45b2Smrg  		m->connector, m->standard, m->chip_family);
114016a45b2Smrg  #endif
115016a45b2Smrg	switch (m->chip_family) {
116016a45b2Smrg		case MSPFAMILY_34x0G:
117016a45b2Smrg			InitMSP34xxG(m);
118016a45b2Smrg			break;
119016a45b2Smrg		case MSPFAMILY_34x5G:
120016a45b2Smrg			InitMSP34xxG(m);
121016a45b2Smrg			break;
122016a45b2Smrg		case MSPFAMILY_34x5D:
123016a45b2Smrg			InitMSP34x5D(m);
124016a45b2Smrg			break;
125016a45b2Smrg	}
126016a45b2Smrg}
127016a45b2Smrg
128016a45b2Smrg/*-----------------------------------------------------------------
129016a45b2Smrg| common functions for all MSP34xx chips
130016a45b2Smrg|----------------------------------------------------------------*/
131016a45b2Smrg
132016a45b2SmrgMSP3430Ptr DetectMSP3430(I2CBusPtr b, I2CSlaveAddr addr)
133016a45b2Smrg{
134016a45b2Smrg   MSP3430Ptr m;
135016a45b2Smrg   I2CByte a;
136016a45b2Smrg   CARD8 hardware_version, major_revision, product_code, rom_version;
137016a45b2Smrg   Bool supported;
138016a45b2Smrg
139016a45b2Smrg   m = calloc(1,sizeof(MSP3430Rec));
140016a45b2Smrg   if(m == NULL)return NULL;
141016a45b2Smrg   m->d.DevName = strdup("MSP34xx");
142016a45b2Smrg   m->d.SlaveAddr = addr;
143016a45b2Smrg   m->d.pI2CBus = b;
144016a45b2Smrg   m->d.NextDev = NULL;
145016a45b2Smrg   m->d.StartTimeout = b->StartTimeout;
146016a45b2Smrg   m->d.BitTimeout = b->BitTimeout;
147016a45b2Smrg   m->d.AcknTimeout = b->AcknTimeout;
148016a45b2Smrg   m->d.ByteTimeout = b->ByteTimeout;
149016a45b2Smrg
150016a45b2Smrg   if(!I2C_WriteRead(&(m->d), NULL, 0, &a, 1))
151016a45b2Smrg   {
152861b9feeSmrg       free(__UNCONST(m->d.DevName));
153016a45b2Smrg       free(m);
154016a45b2Smrg	return NULL;
155016a45b2Smrg    }
156016a45b2Smrg
157016a45b2Smrg
158016a45b2Smrg	m->standard=MSP3430_NTSC;
159016a45b2Smrg	m->connector=MSP3430_CONNECTOR_1;
160016a45b2Smrg	m->mode=MSPMODE_STEREO_A;	/*stereo or chanel A if avail. */
161016a45b2Smrg    	m->c_format=MSPFORMAT_UNKNOWN;
162016a45b2Smrg    	m->c_standard=MSPSTANDARD_UNKNOWN;
163016a45b2Smrg    	m->c_matrix=m->c_fmmatrix=m->c_source=0;
164016a45b2Smrg	m->volume=0;
165016a45b2Smrg	m->recheck=FALSE;
166016a45b2Smrg
167016a45b2Smrg   GetMSP3430Data(m, RD_DSP, 0x00, 0x1E, &hardware_version, &major_revision);
168016a45b2Smrg   GetMSP3430Data(m, RD_DSP, 0x00, 0x1F, &product_code, &rom_version);
169016a45b2Smrg   m->hardware_version=hardware_version;
170016a45b2Smrg   m->major_revision=major_revision;
171016a45b2Smrg   m->product_code=product_code;
172016a45b2Smrg   m->rom_version=rom_version;
173016a45b2Smrg
174016a45b2Smrg   m->chip_id=((major_revision << 8) | product_code);
175016a45b2Smrg
176016a45b2Smrg   supported=FALSE;
177016a45b2Smrg   switch (major_revision) {
178016a45b2Smrg   case 4:	/* 34xxD */
179016a45b2Smrg      switch (product_code) {
180016a45b2Smrg	  case 0x05: /* 3405D */
181016a45b2Smrg	  case 0x0A: /* 3410D */
182016a45b2Smrg	  case 0x0F: /* 3415D */
183016a45b2Smrg	  	m->chip_family=MSPFAMILY_34x5D;
184016a45b2Smrg		m->recheck=TRUE;
185016a45b2Smrg		supported=TRUE;
186016a45b2Smrg		break;
187016a45b2Smrg      default:
188016a45b2Smrg	  	m->chip_family=MSPFAMILY_34x0D;
189016a45b2Smrg      }
190016a45b2Smrg	  break;
191016a45b2Smrg   case 7:	/* 34xxG */
192016a45b2Smrg   	switch(product_code){
193016a45b2Smrg		case 0x00:
194016a45b2Smrg		case 0x0A:
195016a45b2Smrg		case 0x1E:
196016a45b2Smrg		case 0x28:
197016a45b2Smrg		case 0x32:
198016a45b2Smrg		  	m->chip_family=MSPFAMILY_34x0G;
199016a45b2Smrg			supported=TRUE;
200016a45b2Smrg			break;
201016a45b2Smrg		case 0x0f:
202016a45b2Smrg		case 0x19:
203016a45b2Smrg		case 0x2d:
204016a45b2Smrg		case 0x37:
205016a45b2Smrg		case 0x41:
206016a45b2Smrg		  	m->chip_family=MSPFAMILY_34x5G;
207016a45b2Smrg			supported=TRUE;
208016a45b2Smrg			#ifdef MSP_DEBUG
209016a45b2Smrg			memset(m->registers_present, 0, 256);
210016a45b2Smrg			#define A(num) m->registers_present[(num)]=1;
211016a45b2Smrg			#define B(num1, num2) memset(&(m->registers_present[num1]), 1, num2-num1);
212016a45b2Smrg			A(0x20)
213016a45b2Smrg			A(0x30)
214016a45b2Smrg			A(0x40)
215016a45b2Smrg			A(0x00)
216016a45b2Smrg			B(0x01, 0x08)
217016a45b2Smrg			B(0x0B, 0x0E)
218016a45b2Smrg			A(0x10)
219016a45b2Smrg			B(0x12,0x14)
220016a45b2Smrg			A(0x16)
221016a45b2Smrg			A(0x29)
222016a45b2Smrg			#undef B
223016a45b2Smrg			#undef A
224016a45b2Smrg			#endif
225016a45b2Smrg			break;
226016a45b2Smrg		default:
227016a45b2Smrg		  	m->chip_family=MSPFAMILY_UNKNOWN;
228016a45b2Smrg		}
229016a45b2Smrg		break;
230016a45b2Smrg   default:
231016a45b2Smrg	  	m->chip_family=MSPFAMILY_UNKNOWN;
232016a45b2Smrg   }
233016a45b2Smrg
234016a45b2Smrg	xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "Found %s%s, rom version 0x%02x, chip_id=0x%04x\n",
235016a45b2Smrg		MSP_getProductName(m->chip_id), supported?"":" (unsupported)", rom_version, m->chip_id);
236016a45b2Smrg
237016a45b2Smrg	if (!supported) {
238861b9feeSmrg            free(__UNCONST(m->d.DevName));
239016a45b2Smrg            free(m);
240016a45b2Smrg            return NULL;
241016a45b2Smrg	}
242016a45b2Smrg   if(!I2CDevInit(&(m->d)))
243016a45b2Smrg   {
244861b9feeSmrg       free(__UNCONST(m->d.DevName));
245016a45b2Smrg       free(m);
246016a45b2Smrg       return NULL;
247016a45b2Smrg   }
248016a45b2Smrg
249016a45b2Smrg   return m;
250016a45b2Smrg}
251016a45b2Smrg
252016a45b2Smrgvoid ResetMSP3430(MSP3430Ptr m)
253016a45b2Smrg{
254016a45b2Smrg    /* Reset the MSP3430 */
255016a45b2Smrg    SetMSP3430Control(m, 0x00, 0x80, 0x00);
256016a45b2Smrg    /* Set it back to normal operation */
257016a45b2Smrg    SetMSP3430Control(m, 0x00, 0x00, 0x00);
258016a45b2Smrg
259016a45b2Smrg    m->c_format=MSPFORMAT_UNKNOWN;
260016a45b2Smrg    m->c_standard=MSPSTANDARD_UNKNOWN;
261016a45b2Smrg    m->c_matrix=m->c_fmmatrix=m->c_source=0;
262016a45b2Smrg	m->volume=0;
263016a45b2Smrg}
264016a45b2Smrg
265016a45b2Smrgvoid MSP3430SetVolume (MSP3430Ptr m, CARD8 value)
266016a45b2Smrg{
267016a45b2Smrg    CARD8 result;
268016a45b2Smrg#if 0
269016a45b2Smrg    CARD8 old_volume;
270016a45b2Smrg    GetMSP3430Data(m, RD_DSP, 0x00, 0x00, &old_volume, &result);
271016a45b2Smrg    xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP3430 result 0x%02x\n", result);
272016a45b2Smrg#endif
273016a45b2Smrg    /* save an extra Get call */
274016a45b2Smrg    result=0;
275016a45b2Smrg
276016a45b2Smrg    SetMSP3430Data(m, WR_DSP, 0x00, 0x00, value, result);
277016a45b2Smrg
278016a45b2Smrg    SetMSP3430Data(m, WR_DSP, 0x00, 0x07, value, 0);
279016a45b2Smrg    m->volume=value;
280016a45b2Smrg
281016a45b2Smrg#if __MSPDEBUG__ > 2
282016a45b2Smrg    MSP3430DumpStatus(m);
283016a45b2Smrg    __MSPBEEP
284016a45b2Smrg    GetMSP3430Data(m, RD_DSP, 0x00, 0x00, &old_volume, &result);
285016a45b2Smrg    xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP3430 volume 0x%02x\n",value);
286016a45b2Smrg#endif
287016a45b2Smrg}
288016a45b2Smrg
289016a45b2Smrg
290016a45b2Smrgvoid MSP3430SetSAP (MSP3430Ptr m, int mode)
291016a45b2Smrg{
292016a45b2Smrg	xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "Put actual code to change SAP here\n");
293016a45b2Smrg
294016a45b2Smrg      SetMSP3430Data(m, WR_DSP, 0x00, 0x08, mode & 0xff, 0x20);
295016a45b2Smrg}
296016a45b2Smrg
297016a45b2Smrg
298016a45b2Smrg#if 0
299016a45b2Smrgvoid MSP3430SetSource(MSP3430Ptr m, CARD8 value)
300016a45b2Smrg{
301016a45b2Smrg    /* Write to DSP, register 0x0008, (loudspeaker channel source/matrix) */
302016a45b2Smrg    /* This sets the source to the TV tuner, for stereo operation */
303016a45b2Smrg    SetMSP3430Data(m, WR_DSP, 0x00, 0x08, value, 0x20);
304016a45b2Smrg}
305016a45b2Smrg#endif
306016a45b2Smrg
307016a45b2Smrg
308016a45b2Smrgchar *MSP_getProductName (CARD16 product_id)
309016a45b2Smrg{
310016a45b2Smrg	switch (product_id) {
311016a45b2Smrg		case 0x0400: return "MSP3400D";
312016a45b2Smrg		case 0x040a: return "MSP3410D";
313016a45b2Smrg		case 0x0405: return "MSP3405D";
314016a45b2Smrg		case 0x040f: return "MSP3415D";
315016a45b2Smrg		case 0x0700: return "MSP3400G";
316016a45b2Smrg		case 0x070a: return "MSP3410G";
317016a45b2Smrg		case 0x071e: return "MSP3430G";
318016a45b2Smrg		case 0x0728: return "MSP3440G";
319016a45b2Smrg		case 0x0732: return "MSP3450G";
320016a45b2Smrg		case 0x070f: return "MSP3415G";
321016a45b2Smrg		case 0x0719: return "MSP3425G";
322016a45b2Smrg		case 0x072d: return "MSP3445G";
323016a45b2Smrg		case 0x0737: return "MSP3455G";
324016a45b2Smrg		case 0x0741: return "MSP3465G";
325016a45b2Smrg	}
326016a45b2Smrg	return "MSP - unknown type";
327016a45b2Smrg}
328016a45b2Smrg
329016a45b2Smrg#if __MSPDEBUG__ > 2
330016a45b2Smrg/*puts beep in MSP output
331016a45b2Smrg    freq = 0x01 - 16Hz ... 0x40 - 1kHz ... 0xff - 4kHz
332016a45b2Smrg*/
333016a45b2Smrgvoid MSPBeep(MSP3430Ptr m, CARD8 freq) {
334016a45b2Smrg    SetMSP3430Data (m, WR_DSP, 0x00, freq, 0x7f, 0x40);
335016a45b2Smrg    mpause(100);
336016a45b2Smrg    SetMSP3430Data (m, WR_DSP, 0x00, 0x14, 0x00, 0x00);
337016a45b2Smrg}
338016a45b2Smrg#endif
339016a45b2Smrg
340016a45b2Smrgvoid mpause(int milliseconds) {
341016a45b2Smrg    int i,m;
342016a45b2Smrg    m=milliseconds/20;
343016a45b2Smrg    for (i=0;i<m;i++) usleep(20000);
344016a45b2Smrg}
345016a45b2Smrg
346016a45b2Smrg/*-----------------------------------------------------------------
347016a45b2Smrg| specific functions for all MSP34xxG chips
348016a45b2Smrg|----------------------------------------------------------------*/
349016a45b2Smrg
350016a45b2Smrgvoid InitMSP34xxG(MSP3430Ptr m)
351016a45b2Smrg{
352016a45b2Smrg
353016a45b2Smrg  #if __MSPDEBUG__ > 1
354016a45b2Smrg  xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"InitMSP34xxG(m->connector=%d, m->standard=%d, m->chip_family=%d)\n",
355016a45b2Smrg  		m->connector, m->standard, m->chip_family);
356016a45b2Smrg  #endif
357016a45b2Smrg   /* Reset MSP3430 */
358016a45b2Smrg   SetMSP3430Control(m, 0x00, 0x80, 0x00);
359016a45b2Smrg   /* Set it back to normal operation */
360016a45b2Smrg   SetMSP3430Control(m, 0x00, 0x00, 0x00);
361016a45b2Smrg
362016a45b2Smrg   /*set MODUS register */
363016a45b2Smrg   /* bits: 0 - automatic sound detection */
364016a45b2Smrg   /*       1 - enable STATUS change */
365016a45b2Smrg   /*       12 - detect 6.5 Mhz carrier as D/K1, D/K2 or D/K NICAM  (does not seem to work ) */
366016a45b2Smrg   /*       13 - detect 4.5 Mhz carrier as BTSC */
367016a45b2Smrg   if ( (m->standard & 0xff) == MSP3430_PAL )
368016a45b2Smrg   {
369016a45b2Smrg      SetMSP3430Data(m, WR_DEM, 0x00, 0x30, 0x30, 0x03|0x08);    /* make O_ pins tristate */
370016a45b2Smrg      /* PAL standard */
371016a45b2Smrg      SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x01); /* possibly wrong */
372016a45b2Smrg   } else {
373016a45b2Smrg      SetMSP3430Data(m, WR_DEM, 0x00, 0x30, 0x20, 0x03|0x08);
374016a45b2Smrg      /* standard selection is M-BTSC-Stereo */
375016a45b2Smrg      SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x20);
376016a45b2Smrg   }
377016a45b2Smrg
378016a45b2Smrg   switch(m->connector){
379016a45b2Smrg         case MSP3430_CONNECTOR_1:
380016a45b2Smrg	        SetMSP3430Data(m, WR_DSP, 0x00, 0x08, 0x03, 0x20);
381016a45b2Smrg	        break;
382016a45b2Smrg	 case MSP3430_CONNECTOR_2:
383016a45b2Smrg		/* this has not been checked yet.. could be bogus */
384016a45b2Smrg    		/* SCART Input Prescale: 0 dB gain */
385016a45b2Smrg		SetMSP3430Data(m, WR_DSP, 0x00, 0x0d, 0x19, 0x00);
386016a45b2Smrg    		SetMSP3430Data(m, WR_DSP, 0x00, 0x08, 0x02, 0x20);
387016a45b2Smrg		break;
388016a45b2Smrg	case MSP3430_CONNECTOR_3:
389016a45b2Smrg	default:
390016a45b2Smrg    		/* SCART Input Prescale: 0 dB gain */
391016a45b2Smrg		SetMSP3430Data(m, WR_DSP, 0x00, 0x0d, 0x19, 0x00);
392016a45b2Smrg
393016a45b2Smrg    		SetMSP3430Data(m, WR_DSP, 0x00, 0x08, 0x02, 0x20);
394016a45b2Smrg    		break;
395016a45b2Smrg	}
396016a45b2Smrg
397016a45b2Smrg    switch(m->standard){
398016a45b2Smrg        case MSP3430_PAL:
399016a45b2Smrg     		SetMSP3430Data(m, WR_DSP, 0x00, 0x0e, 0x24, 0x03);
400016a45b2Smrg     		SetMSP3430Data(m, WR_DSP, 0x00, 0x10, 0x00, 0x5a);
401016a45b2Smrg                SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x03);
402016a45b2Smrg                /* Set volume to FAST_MUTE. */
403016a45b2Smrg     	        SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);
404016a45b2Smrg	        break;
405016a45b2Smrg        case MSP3430_PAL_DK1:
406016a45b2Smrg     		SetMSP3430Data(m, WR_DSP, 0x00, 0x0e, 0x24, 0x03);
407016a45b2Smrg     		SetMSP3430Data(m, WR_DSP, 0x00, 0x10, 0x00, 0x5a);
408016a45b2Smrg		SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x04);
409016a45b2Smrg                /* Set volume to FAST_MUTE. */
410016a45b2Smrg     	        SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);
411016a45b2Smrg		break;
412016a45b2Smrg	case MSP3430_SECAM: /* is this right ? */
413016a45b2Smrg        case MSP3430_NTSC:
414016a45b2Smrg                /* Write to DSP, register 0x000E, (prescale FM/FM matrix) */
415016a45b2Smrg                SetMSP3430Data(m, WR_DSP, 0x00, 0x0e, 0x24, 0x03);
416016a45b2Smrg
417016a45b2Smrg                /* Set volume to FAST_MUTE. */
418016a45b2Smrg                SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);
419016a45b2Smrg		break;
420016a45b2Smrg	}
421016a45b2Smrg
422016a45b2Smrg}
423016a45b2Smrg
424016a45b2Smrg/*-----------------------------------------------------------------
425016a45b2Smrg| specific functions for all MSP34x5D chips
426016a45b2Smrg|----------------------------------------------------------------*/
427016a45b2Smrg
428016a45b2Smrgvoid InitMSP34x5D(MSP3430Ptr m)
429016a45b2Smrg{
430016a45b2Smrgint count;
431016a45b2SmrgCARD8 high,low;
432016a45b2SmrgCARD16 result,standard;
433016a45b2SmrgCARD16 peak;
434016a45b2Smrg
435016a45b2Smrg
436016a45b2Smrgif (m->c_format==MSPFORMAT_UNKNOWN) ResetMSP3430(m);
437016a45b2Smrgelse {
438016a45b2Smrg    /*mute volume*/
439016a45b2Smrg    SetMSP3430Data (m, WR_DSP, 0x00, 0x00, 0x00, 0x00);
440016a45b2Smrg}
441016a45b2Smrg
442016a45b2Smrg
443016a45b2Smrg
444016a45b2Smrg    switch(m->connector){
445016a45b2Smrg	case MSP3430_CONNECTOR_2:
446016a45b2Smrg	case MSP3430_CONNECTOR_3:
447016a45b2Smrg	    if (m->c_format!=MSPFORMAT_SCART) {
448016a45b2Smrg    		/* SCART Input Prescale: 0 dB gain */
449016a45b2Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x0d, 0x19, 0x00);
450016a45b2Smrg			/* this has not been checked yet.. could be bogus */
451016a45b2Smrg			m->c_format=MSPFORMAT_SCART;	/*stereo*/
452016a45b2Smrg		}
453016a45b2Smrg	    break;
454016a45b2Smrg	case MSP3430_CONNECTOR_1:
455016a45b2Smrg	default:
456016a45b2Smrg
457016a45b2Smrg	    switch ( m->standard & 0x00ff ) {
458016a45b2Smrg		case MSP3430_PAL:
459016a45b2Smrg			switch( m->standard ) {
460016a45b2Smrg			case MSP3430_PAL_DK1:
461016a45b2Smrg			    standard=MSPSTANDARD_FM_DK1;
462016a45b2Smrg			    break;
463016a45b2Smrg/*			case MSP3430_PAL_DK2:
464016a45b2Smrg			    standard=MSPSTANDARD_FM_DK2;
465016a45b2Smrg			    break;
466016a45b2Smrg			case MSP3430_PAL_BG:
467016a45b2Smrg			may be FM stereo (Germany) or FM NICAM (Scandinavia,spain)
468016a45b2Smrg			    standard=MSPSTANDARD_AUTO;
469016a45b2Smrg			    break;
470016a45b2Smrg*/
471016a45b2Smrg			default:
472016a45b2Smrg			    standard=MSPSTANDARD_AUTO;
473016a45b2Smrg			}
474016a45b2Smrg		    break;
475016a45b2Smrg		case MSP3430_SECAM:
476016a45b2Smrg		    standard=MSPSTANDARD_AUTO;
477016a45b2Smrg		case MSP3430_NTSC:
478016a45b2Smrg			    /* Only MSP34x5 supported format - Korean NTSC-M*/
479016a45b2Smrg			 standard=MSPSTANDARD_FM_M;
480016a45b2Smrg		default:
481016a45b2Smrg		    standard=MSPSTANDARD_AUTO;
482016a45b2Smrg		}
483016a45b2Smrg
484016a45b2Smrg	    /*no NICAM support in MSP3410D - force to autodetect*/
485016a45b2Smrg	    if ((m->chip_id==0x405) && (standard>=MSPSTANDARD_NICAM_BG))
486016a45b2Smrg    		standard=MSPSTANDARD_AUTO;
487016a45b2Smrg
488016a45b2Smrg	    if (m->c_standard != standard) {
489016a45b2Smrg
490016a45b2Smrg   	        SetMSP3430Data (m, WR_DEM, 0x00, 0x20, standard>>8, standard & 0xFF);
491016a45b2Smrg	        if (standard==MSPSTANDARD_AUTO) {
492016a45b2Smrg			    count = 50; /* time shouldn't exceed 1s, just in case */
493016a45b2Smrg			    do {
494016a45b2Smrg     		        usleep(20000);
495016a45b2Smrg     		        GetMSP3430Data (m, RD_DEM, 0x00, 0x7e, &high, &low);
496016a45b2Smrg			        result = ( high << 8 ) | low;
497016a45b2Smrg     		        --count;
498016a45b2Smrg	    	    } while( result > 0x07ff && count > 0 );
499016a45b2Smrg
500016a45b2Smrg		    	if ((result > MSPSTANDARD_AUTO))
501016a45b2Smrg					standard=result;
502016a45b2Smrg		    	else standard=MSPSTANDARD_UNKNOWN;
503016a45b2Smrg#if __MSPDEBUG__ > 1
504016a45b2Smrg				xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Detected audio standard: %d\n",result);
505016a45b2Smrg#endif
506016a45b2Smrg		    	/* result = MSPSTANDARD_NICAM_L can be one of:
507016a45b2Smrg		    	SECAM_L - MSPSTANDARD_NICAM_L
508016a45b2Smrg		    	D/K1 - MSPSTANDARD_FM_DK1
509016a45b2Smrg		    	D/K2 - MSPSTANDARD_FM_DK2
510016a45b2Smrg		    	D/K-NICAM - MSPSTANDARD_NICAM_DK*/
511016a45b2Smrg		    	if( standard == MSPSTANDARD_NICAM_L ) {
512016a45b2Smrg		        	if ((m->standard & 0x00ff)==MSP3430_PAL) {
513016a45b2Smrg			    		/* force PAL D/K  */
514016a45b2Smrg			    		standard=MSPSTANDARD_FM_DK1;
515016a45b2Smrg		            	SetMSP3430Data (m, WR_DEM, 0x00, 0x20, standard>>8, standard & 0xFF);
516016a45b2Smrg#if __MSPDEBUG__ > 1
517016a45b2Smrg			        	xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO, "Detected 6.5MHz carrier - forced to D/K1 !!!\n" );
518016a45b2Smrg#endif
519016a45b2Smrg					}
520016a45b2Smrg		    	}
521016a45b2Smrg			}
522016a45b2Smrg			m->c_standard=standard;
523016a45b2Smrg	    } /*end - standard changed*/
524016a45b2Smrg	    else {
525016a45b2Smrg			if (standard<MSPSTANDARD_NICAM_BG) {
526016a45b2Smrg    		    /* get old value of ident. mode register*/
527016a45b2Smrg		    	GetMSP3430Data (m, RD_DSP, 0x00, 0x15, &high, &low);
528016a45b2Smrg    		    /* reset Ident-Filter */
529016a45b2Smrg    		    SetMSP3430Data (m, WR_DSP, 0x00, 0x14, 0x00, 0x3F);
530016a45b2Smrg		    	/* put back old value to ident. mode register*/
531016a45b2Smrg    		    SetMSP3430Data (m, WR_DSP, 0x00, 0x14, 0x00, low);
532016a45b2Smrg			}
533016a45b2Smrg	    }
534016a45b2Smrg
535016a45b2Smrg	    if (standard<=MSPSTANDARD_AUTO) {
536016a45b2Smrg    	   	m->c_format=MSPFORMAT_1xFM;
537016a45b2Smrg	    }
538016a45b2Smrg	    else if (standard<MSPSTANDARD_NICAM_BG) {
539016a45b2Smrg			/* set FM prescale */
540016a45b2Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, 0x30, 0);
541016a45b2Smrg			/* set FM deemphasis*/
542016a45b2Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x0f, ((standard==MSPSTANDARD_FM_M)?0:1), 0);
543016a45b2Smrg
544016a45b2Smrg			/* check if FM2 carrier is present */
545016a45b2Smrg			/*turn off FM DC Notch*/
546016a45b2Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x17, 0x00, 0x3f);
547016a45b2Smrg			/*matrix source for Quasi-Peak Detector - stereo: ch2->L ch1->R*/
548016a45b2Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x0c, 0x00, 0x20);
549016a45b2Smrg
550016a45b2Smrg			mpause(250);
551016a45b2Smrg   		    GetMSP3430Data (m, RD_DSP, 0x00, 0x1A, &high, &low);
552016a45b2Smrg			peak = (high << 8) | low;
553016a45b2Smrg#if __MSPDEBUG__ > 1
554016a45b2Smrg			xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Second carrier Quasi-Peak detection: %d\n",peak);
555016a45b2Smrg#endif
556016a45b2Smrg			/*turn on FM DC Notch*/
557016a45b2Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x17, 0x00, 0x00);
558016a45b2Smrg
559016a45b2Smrg			if (peak<5) {
560016a45b2Smrg	    	    /* if second carrier not detected - only mono from first carrier*/
561016a45b2Smrg	    	    m->c_format=MSPFORMAT_1xFM;
562016a45b2Smrg			}
563016a45b2Smrg			else {
564016a45b2Smrg	    	    m->c_format=MSPFORMAT_2xFM;
565016a45b2Smrg    	   	    /*start of FM identification process - FM_WAIT
566016a45b2Smrg	    	    wait at least 0.5s - used 1s - gives beter resolution*/
567016a45b2Smrg    	   	    mpause(1000);
568016a45b2Smrg			}
569016a45b2Smrg	    }
570016a45b2Smrg	    else {
571016a45b2Smrg			if (standard==MSPSTANDARD_NICAM_L) {
572016a45b2Smrg	    	    m->c_format=MSPFORMAT_NICAM_AM;
573016a45b2Smrg		    	/* set AM prescale */
574016a45b2Smrg		    	SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, 0x7C, 0);
575016a45b2Smrg			}
576016a45b2Smrg			else {
577016a45b2Smrg	    	    m->c_format=MSPFORMAT_NICAM_FM;
578016a45b2Smrg		    	/* set FM prescale */
579016a45b2Smrg			    SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, 0x30, 0);
580016a45b2Smrg			}
581016a45b2Smrg			/* set FM deemphasis*/
582016a45b2Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x0f, 0x00, 0);
583016a45b2Smrg			/* set NICAM prescale to 0dB */
584016a45b2Smrg			SetMSP3430Data (m, WR_DSP, 0x00, 0x10, 0x20, 0);
585016a45b2Smrg	    }
586016a45b2Smrg
587016a45b2Smrg	    break;
588016a45b2Smrg	} /*end - case conector*/
589016a45b2Smrg
590016a45b2Smrg    CheckModeMSP34x5D(m);
591016a45b2Smrg
592016a45b2Smrg    /* Set volume to FAST_MUTE. */
593016a45b2Smrg    /*SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);*/
594016a45b2Smrg    /*set volume*/
595016a45b2Smrg	MSP3430SetVolume(m,m->volume);
596016a45b2Smrg
597016a45b2Smrg    __MSPBEEP
598016a45b2Smrg
599016a45b2Smrg
600016a45b2Smrg} /* EnableMSP34x5D ()... */
601016a45b2Smrg
602016a45b2Smrg
603016a45b2Smrg
604016a45b2Smrg
605016a45b2Smrgvoid CheckModeMSP34x5D(MSP3430Ptr m) {
606016a45b2Smrg    const char stereo_on=25;
607016a45b2Smrg    const char stereo_off=20;
608016a45b2Smrg    const char dual_on=-stereo_on;
609016a45b2Smrg    const char dual_off=-stereo_off;
610016a45b2Smrg    char detect;
611016a45b2Smrg    CARD8 matrix, fmmatrix, source, high, low;
612016a45b2Smrg
613016a45b2Smrg    fmmatrix=0;		/*no matrix*/
614016a45b2Smrg    source=0;		/*FM*/
615016a45b2Smrg    switch (m->c_format) {
616016a45b2Smrg	case MSPFORMAT_NICAM_FM:
617016a45b2Smrg	case MSPFORMAT_NICAM_AM:
618016a45b2Smrg	case MSPFORMAT_SCART:
619016a45b2Smrg	    source=( (m->c_format == MSPFORMAT_SCART)?2:1 );
620016a45b2Smrg	    switch (m->mode) {
621016a45b2Smrg		case MSPMODE_MONO:
622016a45b2Smrg		    matrix=0x30;	/*MONO*/
623016a45b2Smrg		    break;
624016a45b2Smrg		case MSPMODE_A:
625016a45b2Smrg		    matrix=0x00;	/*A*/
626016a45b2Smrg		    break;
627016a45b2Smrg		case MSPMODE_B:
628016a45b2Smrg		    matrix=0x10;	/*B*/
629016a45b2Smrg		    break;
630016a45b2Smrg		default:
631016a45b2Smrg		    matrix=0x20;	/*STEREO*/
632016a45b2Smrg		    break;
633016a45b2Smrg		}
634016a45b2Smrg	    break;
635016a45b2Smrg	default:
636016a45b2Smrg	case MSPFORMAT_1xFM:
637016a45b2Smrg	    matrix=0x00;	/*A*/
638016a45b2Smrg	    break;
639016a45b2Smrg	case MSPFORMAT_2xFM:
640016a45b2Smrg	    switch (m->mode) {
641016a45b2Smrg		case MSPMODE_MONO:
642016a45b2Smrg		    matrix=0x30;	/*MONO*/
643016a45b2Smrg		    break;
644016a45b2Smrg		case MSPMODE_STEREO:
645016a45b2Smrg		    matrix=0x20;	/*STEREO*/
646016a45b2Smrg		    fmmatrix=((m->c_standard==MSPSTANDARD_FM_M)?2:1);
647016a45b2Smrg		    break;
648016a45b2Smrg		case MSPMODE_AB:
649016a45b2Smrg		    matrix=0x20;	/*STEREO*/
650016a45b2Smrg		    break;
651016a45b2Smrg		case MSPMODE_A:
652016a45b2Smrg		    matrix=0x00;	/*A*/
653016a45b2Smrg		    break;
654016a45b2Smrg		case MSPMODE_B:
655016a45b2Smrg		    matrix=0x10;	/*B*/
656016a45b2Smrg		    break;
657016a45b2Smrg		default:
658016a45b2Smrg    		/*FM_IDENT_CHECK*/
659016a45b2Smrg    		GetMSP3430Data (m, RD_DSP, 0x00, 0x18, &high, &low);
660016a45b2Smrg    		detect=(char)high;
661016a45b2Smrg#if __MSPDEBUG__ > 1
662016a45b2Smrg    		xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Stereo Detection Register: %d\n",detect);
663016a45b2Smrg#endif
664016a45b2Smrg    		if (detect>=((m->c_mode==MSPMODE_STEREO)?stereo_off:stereo_on)) {
665016a45b2Smrg				m->c_mode=MSPMODE_STEREO;
666016a45b2Smrg				matrix=0x20;	/*STEREO*/
667016a45b2Smrg				fmmatrix=((m->c_standard==MSPSTANDARD_FM_M)?2:1);
668016a45b2Smrg		    }
669016a45b2Smrg			else if (detect<=((m->c_mode==MSPMODE_AB)?dual_off:dual_on)) {
670016a45b2Smrg				m->c_mode=MSPMODE_AB;
671016a45b2Smrg    			switch (m->mode) {
672016a45b2Smrg			    case MSPMODE_STEREO_AB: matrix=0x20; break;
673016a45b2Smrg			    case MSPMODE_STEREO_B: matrix=0x10; break;
674016a45b2Smrg			    default:
675016a45b2Smrg				case MSPMODE_A: matrix=0x00; break;
676016a45b2Smrg				}
677016a45b2Smrg			}
678016a45b2Smrg    		else {
679016a45b2Smrg				m->c_mode=MSPMODE_MONO;
680016a45b2Smrg				matrix=0x30;	/*MONO*/
681016a45b2Smrg			}
682016a45b2Smrg		    break;
683016a45b2Smrg	    } /* end - case mode*/
684016a45b2Smrg	    break;
685016a45b2Smrg    }
686016a45b2Smrg
687016a45b2Smrg    if (m->c_fmmatrix != fmmatrix) {
688016a45b2Smrg        GetMSP3430Data (m, RD_DSP, 0x00, 0x0e, &high, &low);
689016a45b2Smrg		SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, high, fmmatrix);
690016a45b2Smrg		m->c_fmmatrix = fmmatrix;
691016a45b2Smrg    }
692016a45b2Smrg
693016a45b2Smrg    if ((m->c_matrix != matrix) || (m->c_source != source)) {
694016a45b2Smrg        /*set chanel source and matrix for loudspeaker*/
695016a45b2Smrg		SetMSP3430Data (m, WR_DSP, 0x00, 0x08, source, matrix);
696016a45b2Smrg
697016a45b2Smrg		m->c_matrix = matrix;
698016a45b2Smrg		m->c_source = source;
699016a45b2Smrg    }
700016a45b2Smrg
701016a45b2Smrg	if ( ((m->c_format) & 0xF0) == MSPFORMAT_NICAM)
702016a45b2Smrg			SetMSP3430Data (m, WR_DEM, 0x00, 0x21, 0, 1);
703016a45b2Smrg
704016a45b2Smrg#if __MSPDEBUG__ > 0
705016a45b2Smrg		    char *msg;
706016a45b2Smrg		    switch (matrix) {
707016a45b2Smrg			case 0x30: /*MONO*/
708016a45b2Smrg			    msg="MONO";
709016a45b2Smrg			    break;
710016a45b2Smrg			case 0x00: /*LEFT*/
711016a45b2Smrg			    msg="MONO/CHANNEL_1";
712016a45b2Smrg			    break;
713016a45b2Smrg			case 0x10: /*RIGHT*/
714016a45b2Smrg			    msg="MONO/CHANNEL_2";
715016a45b2Smrg			    break;
716016a45b2Smrg			case 0x20: /*LEFT*/
717016a45b2Smrg			    msg="STEREO";
718016a45b2Smrg			    break;
719016a45b2Smrg			default:
720016a45b2Smrg			    msg="unknown";
721016a45b2Smrg			    break;
722016a45b2Smrg		    }
723016a45b2Smrg    		    xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Audio mode set to: %s\n",msg);
724016a45b2Smrg#endif
725016a45b2Smrg}
726016a45b2Smrg
727