xref: /dragonfly/sys/dev/video/cxm/cxm_saa7115.c (revision 86d7f5d3)
1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino  * Copyright (c) 2003, 2004, 2005
3*86d7f5d3SJohn Marino  *	John Wehle <john@feith.com>.  All rights reserved.
4*86d7f5d3SJohn Marino  *
5*86d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
6*86d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
7*86d7f5d3SJohn Marino  * are met:
8*86d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
9*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
10*86d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
11*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
12*86d7f5d3SJohn Marino  *    documentation and/or other materials provided with the distribution.
13*86d7f5d3SJohn Marino  * 3. All advertising materials mentioning features or use of this software
14*86d7f5d3SJohn Marino  *    must display the following acknowledgement:
15*86d7f5d3SJohn Marino  *	This product includes software developed by John Wehle.
16*86d7f5d3SJohn Marino  * 4. The name of the author may not be used to endorse or promote products
17*86d7f5d3SJohn Marino  *    derived from this software without specific prior written permission.
18*86d7f5d3SJohn Marino  *
19*86d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20*86d7f5d3SJohn Marino  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21*86d7f5d3SJohn Marino  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22*86d7f5d3SJohn Marino  * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23*86d7f5d3SJohn Marino  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24*86d7f5d3SJohn Marino  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25*86d7f5d3SJohn Marino  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26*86d7f5d3SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27*86d7f5d3SJohn Marino  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28*86d7f5d3SJohn Marino  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29*86d7f5d3SJohn Marino  * POSSIBILITY OF SUCH DAMAGE.
30*86d7f5d3SJohn Marino  */
31*86d7f5d3SJohn Marino 
32*86d7f5d3SJohn Marino /*
33*86d7f5d3SJohn Marino  * Video decoder routines for the Conexant MPEG-2 Codec driver.
34*86d7f5d3SJohn Marino  *
35*86d7f5d3SJohn Marino  * Ideally these routines should be implemented as a separate
36*86d7f5d3SJohn Marino  * driver which has a generic video decoder interface so that
37*86d7f5d3SJohn Marino  * it's not necessary for each multimedia driver to re-invent
38*86d7f5d3SJohn Marino  * the wheel.
39*86d7f5d3SJohn Marino  */
40*86d7f5d3SJohn Marino 
41*86d7f5d3SJohn Marino #include <sys/param.h>
42*86d7f5d3SJohn Marino #include <sys/systm.h>
43*86d7f5d3SJohn Marino #include <sys/conf.h>
44*86d7f5d3SJohn Marino #include <sys/uio.h>
45*86d7f5d3SJohn Marino #include <sys/kernel.h>
46*86d7f5d3SJohn Marino #include <sys/poll.h>
47*86d7f5d3SJohn Marino #include <sys/select.h>
48*86d7f5d3SJohn Marino #include <sys/resource.h>
49*86d7f5d3SJohn Marino #include <sys/bus.h>
50*86d7f5d3SJohn Marino #include <sys/rman.h>
51*86d7f5d3SJohn Marino 
52*86d7f5d3SJohn Marino #include <machine/clock.h>
53*86d7f5d3SJohn Marino 
54*86d7f5d3SJohn Marino #include <dev/video/cxm/cxm.h>
55*86d7f5d3SJohn Marino 
56*86d7f5d3SJohn Marino #include <bus/iicbus/iiconf.h>
57*86d7f5d3SJohn Marino #include <bus/iicbus/iicbus.h>
58*86d7f5d3SJohn Marino 
59*86d7f5d3SJohn Marino #include "iicbb_if.h"
60*86d7f5d3SJohn Marino 
61*86d7f5d3SJohn Marino 
62*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
63*86d7f5d3SJohn Marino saa7115_init = {
64*86d7f5d3SJohn Marino 	19,
65*86d7f5d3SJohn Marino 	{
66*86d7f5d3SJohn Marino 		/* Full auto mode for CVBS */
67*86d7f5d3SJohn Marino 		{ 0x01, 1, { 0x08 } },
68*86d7f5d3SJohn Marino 		{ 0x03, 18, { 0x20, 0x90, 0x90, 0xeb, 0xe0, 0xb0, 0x40, 0x80,
69*86d7f5d3SJohn Marino 			      0x44, 0x40, 0x00, 0x03, 0x2a, 0x06, 0x00, 0x9d,
70*86d7f5d3SJohn Marino 			      0x80, 0x01 } },
71*86d7f5d3SJohn Marino 		{ 0x17, 7, { 0x99, 0x40, 0x80, 0x77, 0x42, 0xa9, 0x01 } },
72*86d7f5d3SJohn Marino 
73*86d7f5d3SJohn Marino 		/*
74*86d7f5d3SJohn Marino 		 * VBI data slicer
75*86d7f5d3SJohn Marino 		 *
76*86d7f5d3SJohn Marino 		 * NTSC raw VBI data on lines 10 through 21
77*86d7f5d3SJohn Marino 		 * PAL raw VBI data on lines 6 through 22
78*86d7f5d3SJohn Marino 		 *
79*86d7f5d3SJohn Marino 		 * Actually lines 21 and 22 are set by the
80*86d7f5d3SJohn Marino 		 * NTSC and PAL specific configurations.
81*86d7f5d3SJohn Marino 		 */
82*86d7f5d3SJohn Marino 		{ 0x40, 20, { 0x40, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xdd, 0xdd,
83*86d7f5d3SJohn Marino 			      0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
84*86d7f5d3SJohn Marino 			      0xdd, 0xdd, 0xdd, 0xdd } },
85*86d7f5d3SJohn Marino 		{ 0x56, 4, { 0x00, 0x00, 0x00, 0x47 } },
86*86d7f5d3SJohn Marino 		{ 0x5c, 3, { 0x00, 0x1f, 0x35 } },
87*86d7f5d3SJohn Marino 
88*86d7f5d3SJohn Marino 		/* I-port and X-port configuration */
89*86d7f5d3SJohn Marino 		{ 0x80, 2, { 0x00, 0x01 } },
90*86d7f5d3SJohn Marino 		{ 0x83, 5, { 0x00, 0x20, 0x21, 0xc5, 0x01 } },
91*86d7f5d3SJohn Marino 
92*86d7f5d3SJohn Marino 		/* Scaler input configuration and output format settings */
93*86d7f5d3SJohn Marino 		{ 0xc0, 4, { 0x00, 0x08, 0x00, 0x80 } },
94*86d7f5d3SJohn Marino 
95*86d7f5d3SJohn Marino 		/* VBI scaler configuration */
96*86d7f5d3SJohn Marino 		{ 0x90, 4, { 0x80, 0x48, 0x00, 0x84 } },
97*86d7f5d3SJohn Marino 		{ 0xa0, 3, { 0x01, 0x00, 0x00 } },
98*86d7f5d3SJohn Marino 		{ 0xa4, 3, { 0x80, 0x40, 0x40 } },
99*86d7f5d3SJohn Marino 		{ 0xa8, 3, { 0x00, 0x02, 0x00 } },
100*86d7f5d3SJohn Marino 		{ 0xac, 3, { 0x00, 0x01, 0x00 } },
101*86d7f5d3SJohn Marino 		{ 0xb0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
102*86d7f5d3SJohn Marino 		{ 0xb8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
103*86d7f5d3SJohn Marino 
104*86d7f5d3SJohn Marino 		/* Audio Master Clock to Audio Serial Clock ratio */
105*86d7f5d3SJohn Marino 		{ 0x38, 3, { 0x03, 0x10, 0x00 } },
106*86d7f5d3SJohn Marino 
107*86d7f5d3SJohn Marino 		/* PLL2 target clock 27 MHz (using a 32.11 MHz crystal) */
108*86d7f5d3SJohn Marino 		{ 0xf1, 4, { 0x05, 0xd0, 0x35, 0x00 } },
109*86d7f5d3SJohn Marino 
110*86d7f5d3SJohn Marino 		/* Pulse generator */
111*86d7f5d3SJohn Marino 		{ 0xf6, 10, { 0x61, 0x0e, 0x60, 0x0e, 0x60, 0x0e, 0x00,
112*86d7f5d3SJohn Marino 			      0x00, 0x00, 0x88 } }
113*86d7f5d3SJohn Marino 	}
114*86d7f5d3SJohn Marino };
115*86d7f5d3SJohn Marino 
116*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
117*86d7f5d3SJohn Marino saa7115_mute = {
118*86d7f5d3SJohn Marino 	1,
119*86d7f5d3SJohn Marino 	{
120*86d7f5d3SJohn Marino 		/* Disable I-port */
121*86d7f5d3SJohn Marino 		{ 0x87, 1, { 0x00 } },
122*86d7f5d3SJohn Marino 	}
123*86d7f5d3SJohn Marino };
124*86d7f5d3SJohn Marino 
125*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
126*86d7f5d3SJohn Marino saa7115_unmute = {
127*86d7f5d3SJohn Marino 	1,
128*86d7f5d3SJohn Marino 	{
129*86d7f5d3SJohn Marino 		/* Enable I-port */
130*86d7f5d3SJohn Marino 		{ 0x87, 1, { 0x01 } },
131*86d7f5d3SJohn Marino 	}
132*86d7f5d3SJohn Marino };
133*86d7f5d3SJohn Marino 
134*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
135*86d7f5d3SJohn Marino saa7115_select_fm = {
136*86d7f5d3SJohn Marino 	1,
137*86d7f5d3SJohn Marino 	{
138*86d7f5d3SJohn Marino 		/* Enable audio clock */
139*86d7f5d3SJohn Marino 		{ 0x88, 1, { 0x33 } }
140*86d7f5d3SJohn Marino 	}
141*86d7f5d3SJohn Marino };
142*86d7f5d3SJohn Marino 
143*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
144*86d7f5d3SJohn Marino saa7115_select_line_in_composite = {
145*86d7f5d3SJohn Marino 	3,
146*86d7f5d3SJohn Marino 	{
147*86d7f5d3SJohn Marino 		/* Amp plus anti-alias filter, CVBS from AI11 */
148*86d7f5d3SJohn Marino 		{ 0x02, 1, { 0xc0 } },
149*86d7f5d3SJohn Marino 		/* Adaptive luminance comb filter */
150*86d7f5d3SJohn Marino 		{ 0x09, 1, { 0x40 } },
151*86d7f5d3SJohn Marino 
152*86d7f5d3SJohn Marino 		/* Enable AD1, audio clock, scaler, decoder */
153*86d7f5d3SJohn Marino 		{ 0x88, 1, { 0x70 } }
154*86d7f5d3SJohn Marino 	}
155*86d7f5d3SJohn Marino };
156*86d7f5d3SJohn Marino 
157*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
158*86d7f5d3SJohn Marino saa7115_select_line_in_svideo = {
159*86d7f5d3SJohn Marino 	3,
160*86d7f5d3SJohn Marino 	{
161*86d7f5d3SJohn Marino 		/* Amp plus anti-alias filter, Y / C from AI11 / AI21 */
162*86d7f5d3SJohn Marino 		{ 0x02, 1, { 0xc8 } },
163*86d7f5d3SJohn Marino 		/* Bypass chrominance trap / comb filter */
164*86d7f5d3SJohn Marino 		{ 0x09, 1, { 0x80 } },
165*86d7f5d3SJohn Marino 
166*86d7f5d3SJohn Marino 		/* Enable AD1 & 2, audio clock, scaler, decoder */
167*86d7f5d3SJohn Marino 		{ 0x88, 1, { 0xf0 } }
168*86d7f5d3SJohn Marino 	}
169*86d7f5d3SJohn Marino };
170*86d7f5d3SJohn Marino 
171*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
172*86d7f5d3SJohn Marino saa7115_select_tuner = {
173*86d7f5d3SJohn Marino 	3,
174*86d7f5d3SJohn Marino 	{
175*86d7f5d3SJohn Marino 		/* Amp plus anti-alias filter, CVBS (auto gain) from AI23 */
176*86d7f5d3SJohn Marino 		{ 0x02, 1, { 0xc4 } },
177*86d7f5d3SJohn Marino 		/* Adaptive luminance comb filter */
178*86d7f5d3SJohn Marino 		{ 0x09, 1, { 0x40 } },
179*86d7f5d3SJohn Marino 
180*86d7f5d3SJohn Marino 		/* Enable AD2, audio clock, scaler, decoder */
181*86d7f5d3SJohn Marino 		{ 0x88, 1, { 0xb0 } }
182*86d7f5d3SJohn Marino 	}
183*86d7f5d3SJohn Marino };
184*86d7f5d3SJohn Marino 
185*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
186*86d7f5d3SJohn Marino saa7115_audio_clock_44100_ntsc = {
187*86d7f5d3SJohn Marino 	2,
188*86d7f5d3SJohn Marino 	{
189*86d7f5d3SJohn Marino 		/* Audio clock 44.1 kHz NTSC (using a 32.11 MHz crystal) */
190*86d7f5d3SJohn Marino 		{ 0x30, 3, { 0xbc, 0xdf, 0x02 } },
191*86d7f5d3SJohn Marino 		{ 0x34, 3, { 0xf2, 0x00, 0x2d } }
192*86d7f5d3SJohn Marino 	}
193*86d7f5d3SJohn Marino };
194*86d7f5d3SJohn Marino 
195*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
196*86d7f5d3SJohn Marino saa7115_audio_clock_44100_pal = {
197*86d7f5d3SJohn Marino 	2,
198*86d7f5d3SJohn Marino 	{
199*86d7f5d3SJohn Marino 		/* Audio clock 44.1 kHz PAL (using a 32.11 MHz crystal) */
200*86d7f5d3SJohn Marino 		{ 0x30, 3, { 0x00, 0x72, 0x03 } },
201*86d7f5d3SJohn Marino 		{ 0x34, 3, { 0xf2, 0x00, 0x2d } }
202*86d7f5d3SJohn Marino 	}
203*86d7f5d3SJohn Marino };
204*86d7f5d3SJohn Marino 
205*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
206*86d7f5d3SJohn Marino saa7115_audio_clock_48000_ntsc = {
207*86d7f5d3SJohn Marino 	2,
208*86d7f5d3SJohn Marino 	{
209*86d7f5d3SJohn Marino 		/* Audio clock 48 kHz NTSC (using a 32.11 MHz crystal) */
210*86d7f5d3SJohn Marino 		{ 0x30, 3, { 0xcd, 0x20, 0x03 } },
211*86d7f5d3SJohn Marino 		{ 0x34, 3, { 0xce, 0xfb, 0x30 } }
212*86d7f5d3SJohn Marino 	}
213*86d7f5d3SJohn Marino };
214*86d7f5d3SJohn Marino 
215*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
216*86d7f5d3SJohn Marino saa7115_audio_clock_48000_pal = {
217*86d7f5d3SJohn Marino 	2,
218*86d7f5d3SJohn Marino 	{
219*86d7f5d3SJohn Marino 		/* Audio clock 48 kHz PAL (using a 32.11 MHz crystal) */
220*86d7f5d3SJohn Marino 		{ 0x30, 3, { 0x00, 0xc0, 0x03 } },
221*86d7f5d3SJohn Marino 		{ 0x34, 3, { 0xce, 0xfb, 0x30 } }
222*86d7f5d3SJohn Marino 	}
223*86d7f5d3SJohn Marino };
224*86d7f5d3SJohn Marino 
225*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
226*86d7f5d3SJohn Marino saa7115_scaler_vcd_ntsc_double_lines = {
227*86d7f5d3SJohn Marino 	13,
228*86d7f5d3SJohn Marino 	{
229*86d7f5d3SJohn Marino 		/*
230*86d7f5d3SJohn Marino 		 * Input window = 720 x 240, output window = 352 x 240 with
231*86d7f5d3SJohn Marino 		 * YS extended by 2 as per section 17.4 of the data sheet
232*86d7f5d3SJohn Marino 		 * and YO accounting for scaler processing triggering at
233*86d7f5d3SJohn Marino 		 * line 5 and active video starting at line 23 (see section
234*86d7f5d3SJohn Marino 		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
235*86d7f5d3SJohn Marino 		 * NTSC active video should actually start at line 22, however
236*86d7f5d3SJohn Marino 		 * not all channels / programs do.
237*86d7f5d3SJohn Marino 		 */
238*86d7f5d3SJohn Marino 		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x12, 0x00, 0xf2, 0x00,
239*86d7f5d3SJohn Marino 			      0x60, 0x01, 0xf0, 0x00 } },
240*86d7f5d3SJohn Marino 
241*86d7f5d3SJohn Marino 		/* Prefiltering and prescaling */
242*86d7f5d3SJohn Marino 		{ 0xd0, 3, { 0x02, 0x02, 0xaa } },
243*86d7f5d3SJohn Marino 
244*86d7f5d3SJohn Marino 		/* Brightness, contrast, and saturation */
245*86d7f5d3SJohn Marino 		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
246*86d7f5d3SJohn Marino 
247*86d7f5d3SJohn Marino 		/* Horizontal phase scaling */
248*86d7f5d3SJohn Marino 		{ 0xd8, 3, { 0x18, 0x04, 0x00 } },
249*86d7f5d3SJohn Marino 		{ 0xdc, 3, { 0x0c, 0x02, 0x00 } },
250*86d7f5d3SJohn Marino 
251*86d7f5d3SJohn Marino 		/* Vertical scaling */
252*86d7f5d3SJohn Marino 		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
253*86d7f5d3SJohn Marino 
254*86d7f5d3SJohn Marino 		/* Vertical phase offsets */
255*86d7f5d3SJohn Marino 		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
256*86d7f5d3SJohn Marino 
257*86d7f5d3SJohn Marino 		/*
258*86d7f5d3SJohn Marino 		 * VBI input window = 720 x 12, output window = 1440 x 12.
259*86d7f5d3SJohn Marino 		 */
260*86d7f5d3SJohn Marino 		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x05, 0x00, 0x0c, 0x00,
261*86d7f5d3SJohn Marino 			      0xa0, 0x05, 0x0c, 0x00 } },
262*86d7f5d3SJohn Marino 
263*86d7f5d3SJohn Marino 		/* Inverted VGATE start at line 23, stop after line 263 */
264*86d7f5d3SJohn Marino 		{ 0x15, 2, { 0x02, 0x12 } },
265*86d7f5d3SJohn Marino 
266*86d7f5d3SJohn Marino 		/* VBI data slicer 525 lines, line 21 is closed caption */
267*86d7f5d3SJohn Marino 		{ 0x54, 2, { 0x4d, 0x00 } },
268*86d7f5d3SJohn Marino 		{ 0x5a, 2, { 0x06, 0x83 } },
269*86d7f5d3SJohn Marino 
270*86d7f5d3SJohn Marino 		/* PLL2 525 lines, 27 Mhz target clock */
271*86d7f5d3SJohn Marino 		{ 0xf0, 1, { 0xad } },
272*86d7f5d3SJohn Marino 
273*86d7f5d3SJohn Marino 		/* Pulse generator 525 lines, 27 Mhz target clock */
274*86d7f5d3SJohn Marino 		{ 0xf5, 1, { 0xad } }
275*86d7f5d3SJohn Marino 	}
276*86d7f5d3SJohn Marino };
277*86d7f5d3SJohn Marino 
278*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
279*86d7f5d3SJohn Marino saa7115_scaler_vcd_pal_double_lines = {
280*86d7f5d3SJohn Marino 	13,
281*86d7f5d3SJohn Marino 	{
282*86d7f5d3SJohn Marino 		/*
283*86d7f5d3SJohn Marino 		 * Input window = 720 x 288, output window = 352 x 288 with
284*86d7f5d3SJohn Marino 		 * YS extended by 2 as per section 17.4 of the data sheet
285*86d7f5d3SJohn Marino 		 * and YO accounting for scaler processing triggering at
286*86d7f5d3SJohn Marino 		 * line 2 and active video starting at line 25 (see section
287*86d7f5d3SJohn Marino 		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
288*86d7f5d3SJohn Marino 		 * PAL active video should actually start at line 24, however
289*86d7f5d3SJohn Marino 		 * not all channels / programs do.
290*86d7f5d3SJohn Marino 		 */
291*86d7f5d3SJohn Marino 		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x17, 0x00, 0x22, 0x01,
292*86d7f5d3SJohn Marino 			      0x60, 0x01, 0x20, 0x01 } },
293*86d7f5d3SJohn Marino 
294*86d7f5d3SJohn Marino 		/* Prefiltering and prescaling */
295*86d7f5d3SJohn Marino 		{ 0xd0, 3, { 0x02, 0x02, 0xaa } },
296*86d7f5d3SJohn Marino 
297*86d7f5d3SJohn Marino 		/* Brightness, contrast, and saturation */
298*86d7f5d3SJohn Marino 		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
299*86d7f5d3SJohn Marino 
300*86d7f5d3SJohn Marino 		/* Horizontal phase scaling */
301*86d7f5d3SJohn Marino 		{ 0xd8, 3, { 0x18, 0x04, 0x00 } },
302*86d7f5d3SJohn Marino 		{ 0xdc, 3, { 0x0c, 0x02, 0x00 } },
303*86d7f5d3SJohn Marino 
304*86d7f5d3SJohn Marino 		/* Vertical scaling */
305*86d7f5d3SJohn Marino 		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
306*86d7f5d3SJohn Marino 
307*86d7f5d3SJohn Marino 		/* Vertical phase offsets */
308*86d7f5d3SJohn Marino 		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
309*86d7f5d3SJohn Marino 
310*86d7f5d3SJohn Marino 		/*
311*86d7f5d3SJohn Marino 		 * VBI input window = 720 x 17, output window = 1440 x 17.
312*86d7f5d3SJohn Marino 		 */
313*86d7f5d3SJohn Marino 		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x04, 0x00, 0x11, 0x00,
314*86d7f5d3SJohn Marino 			      0xa0, 0x05, 0x11, 0x00 } },
315*86d7f5d3SJohn Marino 
316*86d7f5d3SJohn Marino 		/* Inverted VGATE start at line 25, stop after line 313 */
317*86d7f5d3SJohn Marino 		{ 0x15, 2, { 0x37, 0x17 } },
318*86d7f5d3SJohn Marino 
319*86d7f5d3SJohn Marino 		/* VBI data slicer 625 lines, line 22 is closed caption */
320*86d7f5d3SJohn Marino 		{ 0x54, 2, { 0xdd, 0x4d } },
321*86d7f5d3SJohn Marino 		{ 0x5a, 2, { 0x03, 0x03 } },
322*86d7f5d3SJohn Marino 
323*86d7f5d3SJohn Marino 		/* PLL2 625 lines, 27 Mhz target clock */
324*86d7f5d3SJohn Marino 		{ 0xf0, 1, { 0xb0 } },
325*86d7f5d3SJohn Marino 
326*86d7f5d3SJohn Marino 		/* Pulse generator 625 lines, 27 Mhz target clock */
327*86d7f5d3SJohn Marino 		{ 0xf5, 1, { 0xb0 } }
328*86d7f5d3SJohn Marino 	}
329*86d7f5d3SJohn Marino };
330*86d7f5d3SJohn Marino 
331*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
332*86d7f5d3SJohn Marino saa7115_scaler_svcd_ntsc = {
333*86d7f5d3SJohn Marino 	13,
334*86d7f5d3SJohn Marino 	{
335*86d7f5d3SJohn Marino 		/*
336*86d7f5d3SJohn Marino 		 * Input window = 720 x 240, output window = 480 x 240 with
337*86d7f5d3SJohn Marino 		 * YS extended by 2 as per section 17.4 of the data sheet
338*86d7f5d3SJohn Marino 		 * and YO accounting for scaler processing triggering at
339*86d7f5d3SJohn Marino 		 * line 5 and active video starting at line 23 (see section
340*86d7f5d3SJohn Marino 		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
341*86d7f5d3SJohn Marino 		 * NTSC active video should actually start at line 22, however
342*86d7f5d3SJohn Marino 		 * not all channels / programs do.
343*86d7f5d3SJohn Marino 		 */
344*86d7f5d3SJohn Marino 		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x12, 0x00, 0xf2, 0x00,
345*86d7f5d3SJohn Marino 			      0xe0, 0x01, 0xf0, 0x00 } },
346*86d7f5d3SJohn Marino 
347*86d7f5d3SJohn Marino 		/* Prefiltering and prescaling */
348*86d7f5d3SJohn Marino 		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
349*86d7f5d3SJohn Marino 
350*86d7f5d3SJohn Marino 		/* Brightness, contrast, and saturation */
351*86d7f5d3SJohn Marino 		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
352*86d7f5d3SJohn Marino 
353*86d7f5d3SJohn Marino 		/* Horizontal phase scaling */
354*86d7f5d3SJohn Marino 		{ 0xd8, 3, { 0x00, 0x06, 0x00 } },
355*86d7f5d3SJohn Marino 		{ 0xdc, 3, { 0x00, 0x03, 0x00 } },
356*86d7f5d3SJohn Marino 
357*86d7f5d3SJohn Marino 		/* Vertical scaling */
358*86d7f5d3SJohn Marino 		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
359*86d7f5d3SJohn Marino 
360*86d7f5d3SJohn Marino 		/* Vertical phase offsets */
361*86d7f5d3SJohn Marino 		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
362*86d7f5d3SJohn Marino 
363*86d7f5d3SJohn Marino 		/*
364*86d7f5d3SJohn Marino 		 * VBI input window = 720 x 12, output window = 1440 x 12.
365*86d7f5d3SJohn Marino 		 */
366*86d7f5d3SJohn Marino 		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x05, 0x00, 0x0c, 0x00,
367*86d7f5d3SJohn Marino 			      0xa0, 0x05, 0x0c, 0x00 } },
368*86d7f5d3SJohn Marino 
369*86d7f5d3SJohn Marino 		/* Inverted VGATE start at line 23, stop after line 263 */
370*86d7f5d3SJohn Marino 		{ 0x15, 2, { 0x02, 0x12 } },
371*86d7f5d3SJohn Marino 
372*86d7f5d3SJohn Marino 		/* VBI data slicer 525 lines, line 21 is closed caption */
373*86d7f5d3SJohn Marino 		{ 0x54, 2, { 0x4d, 0x00 } },
374*86d7f5d3SJohn Marino 		{ 0x5a, 2, { 0x06, 0x83 } },
375*86d7f5d3SJohn Marino 
376*86d7f5d3SJohn Marino 		/* PLL2 525 lines, 27 Mhz target clock */
377*86d7f5d3SJohn Marino 		{ 0xf0, 1, { 0xad } },
378*86d7f5d3SJohn Marino 
379*86d7f5d3SJohn Marino 		/* Pulse generator 525 lines, 27 Mhz target clock */
380*86d7f5d3SJohn Marino 		{ 0xf5, 1, { 0xad } }
381*86d7f5d3SJohn Marino 	}
382*86d7f5d3SJohn Marino };
383*86d7f5d3SJohn Marino 
384*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
385*86d7f5d3SJohn Marino saa7115_scaler_svcd_pal = {
386*86d7f5d3SJohn Marino 	13,
387*86d7f5d3SJohn Marino 	{
388*86d7f5d3SJohn Marino 		/*
389*86d7f5d3SJohn Marino 		 * Input window = 720 x 288, output window = 480 x 288 with
390*86d7f5d3SJohn Marino 		 * YS extended by 2 as per section 17.4 of the data sheet
391*86d7f5d3SJohn Marino 		 * and YO accounting for scaler processing triggering at
392*86d7f5d3SJohn Marino 		 * line 2 and active video starting at line 25 (see section
393*86d7f5d3SJohn Marino 		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
394*86d7f5d3SJohn Marino 		 * PAL active video should actually start at line 24, however
395*86d7f5d3SJohn Marino 		 * not all channels / programs do.
396*86d7f5d3SJohn Marino 		 */
397*86d7f5d3SJohn Marino 		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x17, 0x00, 0x22, 0x01,
398*86d7f5d3SJohn Marino 			      0xe0, 0x01, 0x20, 0x01 } },
399*86d7f5d3SJohn Marino 
400*86d7f5d3SJohn Marino 		/* Prefiltering and prescaling */
401*86d7f5d3SJohn Marino 		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
402*86d7f5d3SJohn Marino 
403*86d7f5d3SJohn Marino 		/* Brightness, contrast, and saturation */
404*86d7f5d3SJohn Marino 		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
405*86d7f5d3SJohn Marino 
406*86d7f5d3SJohn Marino 		/* Horizontal phase scaling */
407*86d7f5d3SJohn Marino 		{ 0xd8, 3, { 0x00, 0x06, 0x00 } },
408*86d7f5d3SJohn Marino 		{ 0xdc, 3, { 0x00, 0x03, 0x00 } },
409*86d7f5d3SJohn Marino 
410*86d7f5d3SJohn Marino 		/* Vertical scaling */
411*86d7f5d3SJohn Marino 		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
412*86d7f5d3SJohn Marino 
413*86d7f5d3SJohn Marino 		/* Vertical phase offsets */
414*86d7f5d3SJohn Marino 		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
415*86d7f5d3SJohn Marino 
416*86d7f5d3SJohn Marino 		/*
417*86d7f5d3SJohn Marino 		 * VBI input window = 720 x 17, output window = 1440 x 17.
418*86d7f5d3SJohn Marino 		 */
419*86d7f5d3SJohn Marino 		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x04, 0x00, 0x11, 0x00,
420*86d7f5d3SJohn Marino 			      0xa0, 0x05, 0x11, 0x00 } },
421*86d7f5d3SJohn Marino 
422*86d7f5d3SJohn Marino 		/* Inverted VGATE start at line 25, stop after line 313 */
423*86d7f5d3SJohn Marino 		{ 0x15, 2, { 0x37, 0x17 } },
424*86d7f5d3SJohn Marino 
425*86d7f5d3SJohn Marino 		/* VBI data slicer 625 lines, line 22 is closed caption */
426*86d7f5d3SJohn Marino 		{ 0x54, 2, { 0xdd, 0x4d } },
427*86d7f5d3SJohn Marino 		{ 0x5a, 2, { 0x03, 0x03 } },
428*86d7f5d3SJohn Marino 
429*86d7f5d3SJohn Marino 		/* PLL2 625 lines, 27 Mhz target clock */
430*86d7f5d3SJohn Marino 		{ 0xf0, 1, { 0xb0 } },
431*86d7f5d3SJohn Marino 
432*86d7f5d3SJohn Marino 		/* Pulse generator 625 lines, 27 Mhz target clock */
433*86d7f5d3SJohn Marino 		{ 0xf5, 1, { 0xb0 } }
434*86d7f5d3SJohn Marino 	}
435*86d7f5d3SJohn Marino };
436*86d7f5d3SJohn Marino 
437*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
438*86d7f5d3SJohn Marino saa7115_scaler_dvd_ntsc = {
439*86d7f5d3SJohn Marino 	13,
440*86d7f5d3SJohn Marino 	{
441*86d7f5d3SJohn Marino 		/*
442*86d7f5d3SJohn Marino 		 * Input window = 720 x 240, output window = 720 x 240 with
443*86d7f5d3SJohn Marino 		 * YS extended by 2 as per section 17.4 of the data sheet
444*86d7f5d3SJohn Marino 		 * and YO accounting for scaler processing triggering at
445*86d7f5d3SJohn Marino 		 * line 5 and active video starting at line 23 (see section
446*86d7f5d3SJohn Marino 		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
447*86d7f5d3SJohn Marino 		 * NTSC active video should actually start at line 22, however
448*86d7f5d3SJohn Marino 		 * not all channels / programs do.
449*86d7f5d3SJohn Marino 		 */
450*86d7f5d3SJohn Marino 		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x12, 0x00, 0xf2, 0x00,
451*86d7f5d3SJohn Marino 			      0xd0, 0x02, 0xf0, 0x00 } },
452*86d7f5d3SJohn Marino 
453*86d7f5d3SJohn Marino 		/* Prefiltering and prescaling */
454*86d7f5d3SJohn Marino 		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
455*86d7f5d3SJohn Marino 
456*86d7f5d3SJohn Marino 		/* Brightness, contrast, and saturation */
457*86d7f5d3SJohn Marino 		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
458*86d7f5d3SJohn Marino 
459*86d7f5d3SJohn Marino 		/* Horizontal phase scaling */
460*86d7f5d3SJohn Marino 		{ 0xd8, 3, { 0x00, 0x04, 0x00 } },
461*86d7f5d3SJohn Marino 		{ 0xdc, 3, { 0x00, 0x02, 0x00 } },
462*86d7f5d3SJohn Marino 
463*86d7f5d3SJohn Marino 		/* Vertical scaling */
464*86d7f5d3SJohn Marino 		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
465*86d7f5d3SJohn Marino 
466*86d7f5d3SJohn Marino 		/* Vertical phase offsets */
467*86d7f5d3SJohn Marino 		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
468*86d7f5d3SJohn Marino 
469*86d7f5d3SJohn Marino 		/*
470*86d7f5d3SJohn Marino 		 * VBI input window = 720 x 12, output window = 1440 x 12.
471*86d7f5d3SJohn Marino 		 */
472*86d7f5d3SJohn Marino 		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x05, 0x00, 0x0c, 0x00,
473*86d7f5d3SJohn Marino 			      0xa0, 0x05, 0x0c, 0x00 } },
474*86d7f5d3SJohn Marino 
475*86d7f5d3SJohn Marino 		/* Inverted VGATE start at line 23, stop after line 263 */
476*86d7f5d3SJohn Marino 		{ 0x15, 2, { 0x02, 0x12 } },
477*86d7f5d3SJohn Marino 
478*86d7f5d3SJohn Marino 		/* VBI data slicer 525 lines, line 21 is closed caption */
479*86d7f5d3SJohn Marino 		{ 0x54, 2, { 0x4d, 0x00 } },
480*86d7f5d3SJohn Marino 		{ 0x5a, 2, { 0x06, 0x83 } },
481*86d7f5d3SJohn Marino 
482*86d7f5d3SJohn Marino 		/* PLL2 525 lines, 27 Mhz target clock */
483*86d7f5d3SJohn Marino 		{ 0xf0, 1, { 0xad } },
484*86d7f5d3SJohn Marino 
485*86d7f5d3SJohn Marino 		/* Pulse generator 525 lines, 27 Mhz target clock */
486*86d7f5d3SJohn Marino 		{ 0xf5, 1, { 0xad } }
487*86d7f5d3SJohn Marino 	}
488*86d7f5d3SJohn Marino };
489*86d7f5d3SJohn Marino 
490*86d7f5d3SJohn Marino static const struct cxm_saa7115_command
491*86d7f5d3SJohn Marino saa7115_scaler_dvd_pal = {
492*86d7f5d3SJohn Marino 	13,
493*86d7f5d3SJohn Marino 	{
494*86d7f5d3SJohn Marino 		/*
495*86d7f5d3SJohn Marino 		 * Input window = 720 x 288, output window = 720 x 288 with
496*86d7f5d3SJohn Marino 		 * YS extended by 2 as per section 17.4 of the data sheet
497*86d7f5d3SJohn Marino 		 * and YO accounting for scaler processing triggering at
498*86d7f5d3SJohn Marino 		 * line 2 and active video starting at line 25 (see section
499*86d7f5d3SJohn Marino 		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
500*86d7f5d3SJohn Marino 		 * PAL active video should actually start at line 24, however
501*86d7f5d3SJohn Marino 		 * not all channels / programs do.
502*86d7f5d3SJohn Marino 		 */
503*86d7f5d3SJohn Marino 		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x17, 0x00, 0x22, 0x01,
504*86d7f5d3SJohn Marino 			      0xd0, 0x02, 0x20, 0x01 } },
505*86d7f5d3SJohn Marino 
506*86d7f5d3SJohn Marino 		/* Prefiltering and prescaling */
507*86d7f5d3SJohn Marino 		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
508*86d7f5d3SJohn Marino 
509*86d7f5d3SJohn Marino 		/* Brightness, contrast, and saturation */
510*86d7f5d3SJohn Marino 		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
511*86d7f5d3SJohn Marino 
512*86d7f5d3SJohn Marino 		/* Horizontal phase scaling */
513*86d7f5d3SJohn Marino 		{ 0xd8, 3, { 0x00, 0x04, 0x00 } },
514*86d7f5d3SJohn Marino 		{ 0xdc, 3, { 0x00, 0x02, 0x00 } },
515*86d7f5d3SJohn Marino 
516*86d7f5d3SJohn Marino 		/* Vertical scaling */
517*86d7f5d3SJohn Marino 		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
518*86d7f5d3SJohn Marino 
519*86d7f5d3SJohn Marino 		/* Vertical phase offsets */
520*86d7f5d3SJohn Marino 		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
521*86d7f5d3SJohn Marino 
522*86d7f5d3SJohn Marino 		/*
523*86d7f5d3SJohn Marino 		 * VBI input window = 720 x 17, output window = 1440 x 17.
524*86d7f5d3SJohn Marino 		 */
525*86d7f5d3SJohn Marino 		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x04, 0x00, 0x11, 0x00,
526*86d7f5d3SJohn Marino 			      0xa0, 0x05, 0x11, 0x00 } },
527*86d7f5d3SJohn Marino 
528*86d7f5d3SJohn Marino 		/* Inverted VGATE start at line 25, stop after line 313 */
529*86d7f5d3SJohn Marino 		{ 0x15, 2, { 0x37, 0x17 } },
530*86d7f5d3SJohn Marino 
531*86d7f5d3SJohn Marino 		/* VBI data slicer 625 lines, line 22 is closed caption */
532*86d7f5d3SJohn Marino 		{ 0x54, 2, { 0xdd, 0x4d } },
533*86d7f5d3SJohn Marino 		{ 0x5a, 2, { 0x03, 0x03 } },
534*86d7f5d3SJohn Marino 
535*86d7f5d3SJohn Marino 		/* PLL2 625 lines, 27 Mhz target clock */
536*86d7f5d3SJohn Marino 		{ 0xf0, 1, { 0xb0 } },
537*86d7f5d3SJohn Marino 
538*86d7f5d3SJohn Marino 		/* Pulse generator 625 lines, 27 Mhz target clock */
539*86d7f5d3SJohn Marino 		{ 0xf5, 1, { 0xb0 } }
540*86d7f5d3SJohn Marino 	}
541*86d7f5d3SJohn Marino };
542*86d7f5d3SJohn Marino 
543*86d7f5d3SJohn Marino 
544*86d7f5d3SJohn Marino static const struct cxm_saa7115_audio_clock
545*86d7f5d3SJohn Marino saa7115_audio_clock[] = {
546*86d7f5d3SJohn Marino 	{ 44100, 30, &saa7115_audio_clock_44100_ntsc },
547*86d7f5d3SJohn Marino 	{ 44100, 25, &saa7115_audio_clock_44100_pal },
548*86d7f5d3SJohn Marino 	{ 48000, 30, &saa7115_audio_clock_48000_ntsc },
549*86d7f5d3SJohn Marino 	{ 48000, 25, &saa7115_audio_clock_48000_pal }
550*86d7f5d3SJohn Marino };
551*86d7f5d3SJohn Marino 
552*86d7f5d3SJohn Marino static const struct cxm_saa7115_scaling
553*86d7f5d3SJohn Marino saa7115_scalings[] = {
554*86d7f5d3SJohn Marino 	{ 352, 480, 30, &saa7115_scaler_vcd_ntsc_double_lines },
555*86d7f5d3SJohn Marino 	{ 352, 576, 25, &saa7115_scaler_vcd_pal_double_lines },
556*86d7f5d3SJohn Marino 	{ 480, 480, 30, &saa7115_scaler_svcd_ntsc },
557*86d7f5d3SJohn Marino 	{ 480, 576, 25, &saa7115_scaler_svcd_pal },
558*86d7f5d3SJohn Marino 	{ 720, 480, 30, &saa7115_scaler_dvd_ntsc },
559*86d7f5d3SJohn Marino 	{ 720, 576, 25, &saa7115_scaler_dvd_pal }
560*86d7f5d3SJohn Marino };
561*86d7f5d3SJohn Marino 
562*86d7f5d3SJohn Marino 
563*86d7f5d3SJohn Marino /* Reset the SAA7115 chip */
564*86d7f5d3SJohn Marino static int
cxm_saa7115_reset(device_t iicbus,int i2c_addr)565*86d7f5d3SJohn Marino cxm_saa7115_reset(device_t iicbus, int i2c_addr)
566*86d7f5d3SJohn Marino {
567*86d7f5d3SJohn Marino 	unsigned char msg[2];
568*86d7f5d3SJohn Marino 	int sent;
569*86d7f5d3SJohn Marino 
570*86d7f5d3SJohn Marino 	/* put into reset mode */
571*86d7f5d3SJohn Marino 	msg[0] = 0x88;
572*86d7f5d3SJohn Marino 	msg[1] = 0x0b;
573*86d7f5d3SJohn Marino 
574*86d7f5d3SJohn Marino 	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
575*86d7f5d3SJohn Marino 		return -1;
576*86d7f5d3SJohn Marino 
577*86d7f5d3SJohn Marino 	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
578*86d7f5d3SJohn Marino 	    || sent != sizeof(msg))
579*86d7f5d3SJohn Marino 		goto fail;
580*86d7f5d3SJohn Marino 
581*86d7f5d3SJohn Marino 	iicbus_stop(iicbus);
582*86d7f5d3SJohn Marino 
583*86d7f5d3SJohn Marino 	/* put back to operational mode */
584*86d7f5d3SJohn Marino 	msg[0] = 0x88;
585*86d7f5d3SJohn Marino 	msg[1] = 0x2b;
586*86d7f5d3SJohn Marino 
587*86d7f5d3SJohn Marino 	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
588*86d7f5d3SJohn Marino 		return -1;
589*86d7f5d3SJohn Marino 
590*86d7f5d3SJohn Marino 	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
591*86d7f5d3SJohn Marino 	    || sent != sizeof(msg))
592*86d7f5d3SJohn Marino 		goto fail;
593*86d7f5d3SJohn Marino 
594*86d7f5d3SJohn Marino 	iicbus_stop(iicbus);
595*86d7f5d3SJohn Marino 
596*86d7f5d3SJohn Marino 	return 0;
597*86d7f5d3SJohn Marino 
598*86d7f5d3SJohn Marino fail:
599*86d7f5d3SJohn Marino 	iicbus_stop(iicbus);
600*86d7f5d3SJohn Marino 	return -1;
601*86d7f5d3SJohn Marino }
602*86d7f5d3SJohn Marino 
603*86d7f5d3SJohn Marino 
604*86d7f5d3SJohn Marino /* Read from the SAA7115 registers */
605*86d7f5d3SJohn Marino static int
cxm_saa7115_read(device_t iicbus,int i2c_addr,unsigned char addr,char * buf,int len)606*86d7f5d3SJohn Marino cxm_saa7115_read(device_t iicbus, int i2c_addr,
607*86d7f5d3SJohn Marino 		  unsigned char addr, char *buf, int len)
608*86d7f5d3SJohn Marino {
609*86d7f5d3SJohn Marino 	unsigned char msg[1];
610*86d7f5d3SJohn Marino 	int received;
611*86d7f5d3SJohn Marino 	int sent;
612*86d7f5d3SJohn Marino 
613*86d7f5d3SJohn Marino 	msg[0] = addr;
614*86d7f5d3SJohn Marino 
615*86d7f5d3SJohn Marino 	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
616*86d7f5d3SJohn Marino 		return -1;
617*86d7f5d3SJohn Marino 
618*86d7f5d3SJohn Marino 	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
619*86d7f5d3SJohn Marino 	    || sent != sizeof(msg))
620*86d7f5d3SJohn Marino 		goto fail;
621*86d7f5d3SJohn Marino 
622*86d7f5d3SJohn Marino 	if (iicbus_repeated_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
623*86d7f5d3SJohn Marino 		goto fail;
624*86d7f5d3SJohn Marino 
625*86d7f5d3SJohn Marino 	if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
626*86d7f5d3SJohn Marino 		goto fail;
627*86d7f5d3SJohn Marino 
628*86d7f5d3SJohn Marino 	iicbus_stop(iicbus);
629*86d7f5d3SJohn Marino 
630*86d7f5d3SJohn Marino 	return received;
631*86d7f5d3SJohn Marino 
632*86d7f5d3SJohn Marino fail:
633*86d7f5d3SJohn Marino 	iicbus_stop(iicbus);
634*86d7f5d3SJohn Marino 	return -1;
635*86d7f5d3SJohn Marino }
636*86d7f5d3SJohn Marino 
637*86d7f5d3SJohn Marino 
638*86d7f5d3SJohn Marino /* Write to the SAA7115 registers */
639*86d7f5d3SJohn Marino static int
cxm_saa7115_write(device_t iicbus,int i2c_addr,unsigned char addr,const char * buf,int len)640*86d7f5d3SJohn Marino cxm_saa7115_write(device_t iicbus, int i2c_addr,
641*86d7f5d3SJohn Marino 		   unsigned char addr, const char *buf, int len)
642*86d7f5d3SJohn Marino {
643*86d7f5d3SJohn Marino 	unsigned char msg[1];
644*86d7f5d3SJohn Marino 	int sent;
645*86d7f5d3SJohn Marino 
646*86d7f5d3SJohn Marino 	msg[0] = addr;
647*86d7f5d3SJohn Marino 
648*86d7f5d3SJohn Marino 	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
649*86d7f5d3SJohn Marino 		return -1;
650*86d7f5d3SJohn Marino 
651*86d7f5d3SJohn Marino 	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
652*86d7f5d3SJohn Marino 	    || sent != sizeof(msg))
653*86d7f5d3SJohn Marino 		goto fail;
654*86d7f5d3SJohn Marino 
655*86d7f5d3SJohn Marino 	if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0)
656*86d7f5d3SJohn Marino 		goto fail;
657*86d7f5d3SJohn Marino 
658*86d7f5d3SJohn Marino 	iicbus_stop(iicbus);
659*86d7f5d3SJohn Marino 
660*86d7f5d3SJohn Marino 	return sent;
661*86d7f5d3SJohn Marino 
662*86d7f5d3SJohn Marino fail:
663*86d7f5d3SJohn Marino 	iicbus_stop(iicbus);
664*86d7f5d3SJohn Marino 	return -1;
665*86d7f5d3SJohn Marino }
666*86d7f5d3SJohn Marino 
667*86d7f5d3SJohn Marino 
668*86d7f5d3SJohn Marino int
cxm_saa7115_init(struct cxm_softc * sc)669*86d7f5d3SJohn Marino cxm_saa7115_init(struct cxm_softc *sc)
670*86d7f5d3SJohn Marino {
671*86d7f5d3SJohn Marino 	char name[5];
672*86d7f5d3SJohn Marino 	unsigned char id[1];
673*86d7f5d3SJohn Marino 	unsigned char rev;
674*86d7f5d3SJohn Marino 	unsigned int i;
675*86d7f5d3SJohn Marino 	unsigned int nsettings;
676*86d7f5d3SJohn Marino 	const struct cxm_saa7115_setting *settings;
677*86d7f5d3SJohn Marino 
678*86d7f5d3SJohn Marino 	if (cxm_saa7115_reset (sc->iicbus, CXM_I2C_SAA7115) < 0)
679*86d7f5d3SJohn Marino 		return -1;
680*86d7f5d3SJohn Marino 
681*86d7f5d3SJohn Marino 	name[4] = '\0';
682*86d7f5d3SJohn Marino 	for (i = 0; i < 4; i++) {
683*86d7f5d3SJohn Marino 		id[0] = 2 + i;
684*86d7f5d3SJohn Marino 
685*86d7f5d3SJohn Marino 		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x00,
686*86d7f5d3SJohn Marino 				      id, sizeof(id)) != sizeof(id))
687*86d7f5d3SJohn Marino 			return -1;
688*86d7f5d3SJohn Marino 
689*86d7f5d3SJohn Marino 		if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x00,
690*86d7f5d3SJohn Marino 				     id, sizeof(id)) != sizeof(id))
691*86d7f5d3SJohn Marino 			return -1;
692*86d7f5d3SJohn Marino 
693*86d7f5d3SJohn Marino 		name[i] = '0' + (id[0] & 0x0f);
694*86d7f5d3SJohn Marino 		rev = id[0] >> 4;
695*86d7f5d3SJohn Marino 	}
696*86d7f5d3SJohn Marino 
697*86d7f5d3SJohn Marino 	/*
698*86d7f5d3SJohn Marino 	 * SAA 7115 is the only video decoder currently supported.
699*86d7f5d3SJohn Marino 	 */
700*86d7f5d3SJohn Marino 
701*86d7f5d3SJohn Marino 	nsettings = 0;
702*86d7f5d3SJohn Marino 	settings = NULL;
703*86d7f5d3SJohn Marino 
704*86d7f5d3SJohn Marino 	if (strcmp(name, "7115") == 0) {
705*86d7f5d3SJohn Marino 		nsettings = saa7115_init.nsettings;
706*86d7f5d3SJohn Marino 		settings = saa7115_init.settings;
707*86d7f5d3SJohn Marino 	} else {
708*86d7f5d3SJohn Marino 		device_printf(sc->dev, "unknown video decoder SAA%s\n", name);
709*86d7f5d3SJohn Marino 		return -1;
710*86d7f5d3SJohn Marino 	}
711*86d7f5d3SJohn Marino 
712*86d7f5d3SJohn Marino 	for (i = 0; i < nsettings; i++)
713*86d7f5d3SJohn Marino 		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
714*86d7f5d3SJohn Marino 				      settings[i].addr,
715*86d7f5d3SJohn Marino 				      settings[i].values, settings[i].nvalues)
716*86d7f5d3SJohn Marino 		    != settings[i].nvalues)
717*86d7f5d3SJohn Marino 			return -1;
718*86d7f5d3SJohn Marino 
719*86d7f5d3SJohn Marino 	if (cxm_saa7115_select_source(sc, cxm_tuner_source) < 0)
720*86d7f5d3SJohn Marino 		return -1;
721*86d7f5d3SJohn Marino 
722*86d7f5d3SJohn Marino 	device_printf(sc->dev, "SAA%s rev %u video decoder\n",
723*86d7f5d3SJohn Marino 	    name, (unsigned int)rev);
724*86d7f5d3SJohn Marino 
725*86d7f5d3SJohn Marino 	return 0;
726*86d7f5d3SJohn Marino }
727*86d7f5d3SJohn Marino 
728*86d7f5d3SJohn Marino 
729*86d7f5d3SJohn Marino int
cxm_saa7115_mute(struct cxm_softc * sc)730*86d7f5d3SJohn Marino cxm_saa7115_mute(struct cxm_softc *sc)
731*86d7f5d3SJohn Marino {
732*86d7f5d3SJohn Marino 	unsigned int i;
733*86d7f5d3SJohn Marino 	unsigned int nsettings;
734*86d7f5d3SJohn Marino 	const struct cxm_saa7115_setting *settings;
735*86d7f5d3SJohn Marino 
736*86d7f5d3SJohn Marino 	nsettings = saa7115_mute.nsettings;
737*86d7f5d3SJohn Marino 	settings = saa7115_mute.settings;
738*86d7f5d3SJohn Marino 
739*86d7f5d3SJohn Marino 	for (i = 0; i < nsettings; i++)
740*86d7f5d3SJohn Marino 		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
741*86d7f5d3SJohn Marino 				      settings[i].addr,
742*86d7f5d3SJohn Marino 				      settings[i].values, settings[i].nvalues)
743*86d7f5d3SJohn Marino 		    != settings[i].nvalues)
744*86d7f5d3SJohn Marino 			return -1;
745*86d7f5d3SJohn Marino 
746*86d7f5d3SJohn Marino 	return 0;
747*86d7f5d3SJohn Marino }
748*86d7f5d3SJohn Marino 
749*86d7f5d3SJohn Marino 
750*86d7f5d3SJohn Marino int
cxm_saa7115_unmute(struct cxm_softc * sc)751*86d7f5d3SJohn Marino cxm_saa7115_unmute(struct cxm_softc *sc)
752*86d7f5d3SJohn Marino {
753*86d7f5d3SJohn Marino 	unsigned int i;
754*86d7f5d3SJohn Marino 	unsigned int nsettings;
755*86d7f5d3SJohn Marino 	const struct cxm_saa7115_setting *settings;
756*86d7f5d3SJohn Marino 
757*86d7f5d3SJohn Marino 	nsettings = saa7115_unmute.nsettings;
758*86d7f5d3SJohn Marino 	settings = saa7115_unmute.settings;
759*86d7f5d3SJohn Marino 
760*86d7f5d3SJohn Marino 	for (i = 0; i < nsettings; i++)
761*86d7f5d3SJohn Marino 		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
762*86d7f5d3SJohn Marino 				      settings[i].addr,
763*86d7f5d3SJohn Marino 				      settings[i].values, settings[i].nvalues)
764*86d7f5d3SJohn Marino 		    != settings[i].nvalues)
765*86d7f5d3SJohn Marino 			return -1;
766*86d7f5d3SJohn Marino 
767*86d7f5d3SJohn Marino 	return 0;
768*86d7f5d3SJohn Marino }
769*86d7f5d3SJohn Marino 
770*86d7f5d3SJohn Marino 
771*86d7f5d3SJohn Marino int
cxm_saa7115_select_source(struct cxm_softc * sc,enum cxm_source source)772*86d7f5d3SJohn Marino cxm_saa7115_select_source(struct cxm_softc *sc, enum cxm_source source)
773*86d7f5d3SJohn Marino {
774*86d7f5d3SJohn Marino 	unsigned int i;
775*86d7f5d3SJohn Marino 	unsigned int nsettings;
776*86d7f5d3SJohn Marino 	const struct cxm_saa7115_setting *settings;
777*86d7f5d3SJohn Marino 
778*86d7f5d3SJohn Marino 	switch (source) {
779*86d7f5d3SJohn Marino 	case cxm_fm_source:
780*86d7f5d3SJohn Marino 		nsettings = saa7115_select_fm.nsettings;
781*86d7f5d3SJohn Marino 		settings = saa7115_select_fm.settings;
782*86d7f5d3SJohn Marino 		break;
783*86d7f5d3SJohn Marino 
784*86d7f5d3SJohn Marino 	case cxm_line_in_source_composite:
785*86d7f5d3SJohn Marino 		nsettings = saa7115_select_line_in_composite.nsettings;
786*86d7f5d3SJohn Marino 		settings = saa7115_select_line_in_composite.settings;
787*86d7f5d3SJohn Marino 		break;
788*86d7f5d3SJohn Marino 
789*86d7f5d3SJohn Marino 	case cxm_line_in_source_svideo:
790*86d7f5d3SJohn Marino 		nsettings = saa7115_select_line_in_svideo.nsettings;
791*86d7f5d3SJohn Marino 		settings = saa7115_select_line_in_svideo.settings;
792*86d7f5d3SJohn Marino 		break;
793*86d7f5d3SJohn Marino 
794*86d7f5d3SJohn Marino 	case cxm_tuner_source:
795*86d7f5d3SJohn Marino 		nsettings = saa7115_select_tuner.nsettings;
796*86d7f5d3SJohn Marino 		settings = saa7115_select_tuner.settings;
797*86d7f5d3SJohn Marino 		break;
798*86d7f5d3SJohn Marino 
799*86d7f5d3SJohn Marino 	default:
800*86d7f5d3SJohn Marino 		return -1;
801*86d7f5d3SJohn Marino 	}
802*86d7f5d3SJohn Marino 
803*86d7f5d3SJohn Marino 	for (i = 0; i < nsettings; i++)
804*86d7f5d3SJohn Marino 		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
805*86d7f5d3SJohn Marino 				      settings[i].addr,
806*86d7f5d3SJohn Marino 				      settings[i].values, settings[i].nvalues)
807*86d7f5d3SJohn Marino 		    != settings[i].nvalues)
808*86d7f5d3SJohn Marino 			return -1;
809*86d7f5d3SJohn Marino 
810*86d7f5d3SJohn Marino 	return 0;
811*86d7f5d3SJohn Marino }
812*86d7f5d3SJohn Marino 
813*86d7f5d3SJohn Marino 
814*86d7f5d3SJohn Marino int
cxm_saa7115_configure(struct cxm_softc * sc,unsigned int width,unsigned int height,unsigned int fps,unsigned int audio_sample_rate)815*86d7f5d3SJohn Marino cxm_saa7115_configure(struct cxm_softc *sc,
816*86d7f5d3SJohn Marino 		       unsigned int width, unsigned int height,
817*86d7f5d3SJohn Marino 		       unsigned int fps, unsigned int audio_sample_rate)
818*86d7f5d3SJohn Marino {
819*86d7f5d3SJohn Marino 	unsigned char power[1];
820*86d7f5d3SJohn Marino 	unsigned char task[1];
821*86d7f5d3SJohn Marino 	unsigned int i;
822*86d7f5d3SJohn Marino 	unsigned int nsettings;
823*86d7f5d3SJohn Marino 	const struct cxm_saa7115_setting *settings;
824*86d7f5d3SJohn Marino 
825*86d7f5d3SJohn Marino 	for (i = 0; NUM_ELEMENTS(saa7115_scalings); i++)
826*86d7f5d3SJohn Marino 		if (saa7115_scalings[i].width == width
827*86d7f5d3SJohn Marino 		    && saa7115_scalings[i].height == height
828*86d7f5d3SJohn Marino 		    && saa7115_scalings[i].fps == fps)
829*86d7f5d3SJohn Marino 			break;
830*86d7f5d3SJohn Marino 
831*86d7f5d3SJohn Marino 	if (i >= NUM_ELEMENTS(saa7115_scalings))
832*86d7f5d3SJohn Marino 		return -1;
833*86d7f5d3SJohn Marino 
834*86d7f5d3SJohn Marino 	nsettings = saa7115_scalings[i].scaling->nsettings;
835*86d7f5d3SJohn Marino 	settings = saa7115_scalings[i].scaling->settings;
836*86d7f5d3SJohn Marino 
837*86d7f5d3SJohn Marino 	/*
838*86d7f5d3SJohn Marino 	 * Reset the scaler.
839*86d7f5d3SJohn Marino 	 */
840*86d7f5d3SJohn Marino 
841*86d7f5d3SJohn Marino 	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x88,
842*86d7f5d3SJohn Marino 			     power, sizeof(power)) != sizeof(power))
843*86d7f5d3SJohn Marino 		return -1;
844*86d7f5d3SJohn Marino 
845*86d7f5d3SJohn Marino 	power[0] &= ~0x20;
846*86d7f5d3SJohn Marino 
847*86d7f5d3SJohn Marino 	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x88,
848*86d7f5d3SJohn Marino 			      power, sizeof(power)) != sizeof(power))
849*86d7f5d3SJohn Marino 		return -1;
850*86d7f5d3SJohn Marino 
851*86d7f5d3SJohn Marino 	/*
852*86d7f5d3SJohn Marino 	 * Configure the scaler.
853*86d7f5d3SJohn Marino 	 */
854*86d7f5d3SJohn Marino 
855*86d7f5d3SJohn Marino 	for (i = 0; i < nsettings; i++)
856*86d7f5d3SJohn Marino 		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
857*86d7f5d3SJohn Marino 				      settings[i].addr,
858*86d7f5d3SJohn Marino 				      settings[i].values, settings[i].nvalues)
859*86d7f5d3SJohn Marino 		    != settings[i].nvalues)
860*86d7f5d3SJohn Marino 			return -1;
861*86d7f5d3SJohn Marino 
862*86d7f5d3SJohn Marino 	/*
863*86d7f5d3SJohn Marino 	 * Enable task register set A and B.
864*86d7f5d3SJohn Marino 	 */
865*86d7f5d3SJohn Marino 
866*86d7f5d3SJohn Marino 	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x80,
867*86d7f5d3SJohn Marino 			     task, sizeof(task)) != sizeof(task))
868*86d7f5d3SJohn Marino 		return -1;
869*86d7f5d3SJohn Marino 
870*86d7f5d3SJohn Marino 	task[0] |= 0x30;
871*86d7f5d3SJohn Marino 
872*86d7f5d3SJohn Marino 	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x80,
873*86d7f5d3SJohn Marino 			      task, sizeof(task)) != sizeof(task))
874*86d7f5d3SJohn Marino 		return -1;
875*86d7f5d3SJohn Marino 
876*86d7f5d3SJohn Marino 	/*
877*86d7f5d3SJohn Marino 	 * Enable the scaler.
878*86d7f5d3SJohn Marino 	 */
879*86d7f5d3SJohn Marino 
880*86d7f5d3SJohn Marino 	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x88,
881*86d7f5d3SJohn Marino 			     power, sizeof(power)) != sizeof(power))
882*86d7f5d3SJohn Marino 		return -1;
883*86d7f5d3SJohn Marino 
884*86d7f5d3SJohn Marino 	power[0] |= 0x20;
885*86d7f5d3SJohn Marino 
886*86d7f5d3SJohn Marino 	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x88,
887*86d7f5d3SJohn Marino 			      power, sizeof(power)) != sizeof(power))
888*86d7f5d3SJohn Marino 		return -1;
889*86d7f5d3SJohn Marino 
890*86d7f5d3SJohn Marino 	/*
891*86d7f5d3SJohn Marino 	 * Configure the audio clock.
892*86d7f5d3SJohn Marino 	 */
893*86d7f5d3SJohn Marino 
894*86d7f5d3SJohn Marino 	for (i = 0; NUM_ELEMENTS(saa7115_audio_clock); i++)
895*86d7f5d3SJohn Marino 		if (saa7115_audio_clock[i].sample_rate == audio_sample_rate
896*86d7f5d3SJohn Marino 		    && saa7115_audio_clock[i].fps == fps)
897*86d7f5d3SJohn Marino 			break;
898*86d7f5d3SJohn Marino 
899*86d7f5d3SJohn Marino 	if (i >= NUM_ELEMENTS(saa7115_audio_clock))
900*86d7f5d3SJohn Marino 		return -1;
901*86d7f5d3SJohn Marino 
902*86d7f5d3SJohn Marino 	nsettings = saa7115_audio_clock[i].clock->nsettings;
903*86d7f5d3SJohn Marino 	settings = saa7115_audio_clock[i].clock->settings;
904*86d7f5d3SJohn Marino 
905*86d7f5d3SJohn Marino 	for (i = 0; i < nsettings; i++)
906*86d7f5d3SJohn Marino 		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
907*86d7f5d3SJohn Marino 				      settings[i].addr,
908*86d7f5d3SJohn Marino 				      settings[i].values, settings[i].nvalues)
909*86d7f5d3SJohn Marino 		    != settings[i].nvalues)
910*86d7f5d3SJohn Marino 			return -1;
911*86d7f5d3SJohn Marino 
912*86d7f5d3SJohn Marino 	return 0;
913*86d7f5d3SJohn Marino }
914*86d7f5d3SJohn Marino 
915*86d7f5d3SJohn Marino 
916*86d7f5d3SJohn Marino enum cxm_source_format
cxm_saa7115_detected_format(struct cxm_softc * sc)917*86d7f5d3SJohn Marino cxm_saa7115_detected_format(struct cxm_softc *sc)
918*86d7f5d3SJohn Marino {
919*86d7f5d3SJohn Marino 	unsigned char status[2];
920*86d7f5d3SJohn Marino 	enum cxm_source_format source_format;
921*86d7f5d3SJohn Marino 
922*86d7f5d3SJohn Marino 	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x1e,
923*86d7f5d3SJohn Marino 			     status, sizeof(status)) != sizeof(status))
924*86d7f5d3SJohn Marino 		return cxm_unknown_source_format;
925*86d7f5d3SJohn Marino 
926*86d7f5d3SJohn Marino 	if (!(status[1] & 0x01)) {
927*86d7f5d3SJohn Marino 		device_printf(sc->dev, "video decoder isn't locked\n");
928*86d7f5d3SJohn Marino 		return cxm_unknown_source_format;
929*86d7f5d3SJohn Marino 	}
930*86d7f5d3SJohn Marino 
931*86d7f5d3SJohn Marino 	source_format = cxm_unknown_source_format;
932*86d7f5d3SJohn Marino 
933*86d7f5d3SJohn Marino 	if (!(status[1] & 0x20)) {
934*86d7f5d3SJohn Marino 		switch (status[0] & 0x03) {
935*86d7f5d3SJohn Marino 		case 0:
936*86d7f5d3SJohn Marino 			source_format = cxm_bw_50hz_source_format;
937*86d7f5d3SJohn Marino 			break;
938*86d7f5d3SJohn Marino 
939*86d7f5d3SJohn Marino 		case 1:
940*86d7f5d3SJohn Marino 			source_format = cxm_ntsc_50hz_source_format;
941*86d7f5d3SJohn Marino 			break;
942*86d7f5d3SJohn Marino 
943*86d7f5d3SJohn Marino 		case 2:
944*86d7f5d3SJohn Marino 			source_format = cxm_pal_50hz_source_format;
945*86d7f5d3SJohn Marino 			break;
946*86d7f5d3SJohn Marino 
947*86d7f5d3SJohn Marino 		case 3:
948*86d7f5d3SJohn Marino 			source_format = cxm_secam_50hz_source_format;
949*86d7f5d3SJohn Marino 			break;
950*86d7f5d3SJohn Marino 
951*86d7f5d3SJohn Marino 		default:
952*86d7f5d3SJohn Marino 			break;
953*86d7f5d3SJohn Marino 		}
954*86d7f5d3SJohn Marino 	} else {
955*86d7f5d3SJohn Marino 		switch (status[0] & 0x03) {
956*86d7f5d3SJohn Marino 		case 0:
957*86d7f5d3SJohn Marino 			source_format = cxm_bw_60hz_source_format;
958*86d7f5d3SJohn Marino 			break;
959*86d7f5d3SJohn Marino 
960*86d7f5d3SJohn Marino 		case 1:
961*86d7f5d3SJohn Marino 			source_format = cxm_ntsc_60hz_source_format;
962*86d7f5d3SJohn Marino 			break;
963*86d7f5d3SJohn Marino 
964*86d7f5d3SJohn Marino 		case 2:
965*86d7f5d3SJohn Marino 			source_format = cxm_pal_60hz_source_format;
966*86d7f5d3SJohn Marino 			break;
967*86d7f5d3SJohn Marino 
968*86d7f5d3SJohn Marino 		default:
969*86d7f5d3SJohn Marino 			break;
970*86d7f5d3SJohn Marino 		}
971*86d7f5d3SJohn Marino 	}
972*86d7f5d3SJohn Marino 
973*86d7f5d3SJohn Marino 	return source_format;
974*86d7f5d3SJohn Marino }
975*86d7f5d3SJohn Marino 
976*86d7f5d3SJohn Marino 
977*86d7f5d3SJohn Marino int
cxm_saa7115_detected_fps(struct cxm_softc * sc)978*86d7f5d3SJohn Marino cxm_saa7115_detected_fps(struct cxm_softc *sc)
979*86d7f5d3SJohn Marino {
980*86d7f5d3SJohn Marino 	unsigned char status[1];
981*86d7f5d3SJohn Marino 
982*86d7f5d3SJohn Marino 	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x1f,
983*86d7f5d3SJohn Marino 			     status, sizeof(status)) != sizeof(status))
984*86d7f5d3SJohn Marino 		return -1;
985*86d7f5d3SJohn Marino 
986*86d7f5d3SJohn Marino 	if (!(status[0] & 0x01)) {
987*86d7f5d3SJohn Marino 		device_printf(sc->dev, "video decoder isn't locked\n");
988*86d7f5d3SJohn Marino 		return -1;
989*86d7f5d3SJohn Marino 	}
990*86d7f5d3SJohn Marino 
991*86d7f5d3SJohn Marino 	return (status[0] & 0x20) ? 30 : 25;
992*86d7f5d3SJohn Marino }
993*86d7f5d3SJohn Marino 
994*86d7f5d3SJohn Marino 
995*86d7f5d3SJohn Marino int
cxm_saa7115_get_brightness(struct cxm_softc * sc)996*86d7f5d3SJohn Marino cxm_saa7115_get_brightness(struct cxm_softc *sc)
997*86d7f5d3SJohn Marino {
998*86d7f5d3SJohn Marino 	unsigned char brightness;
999*86d7f5d3SJohn Marino 
1000*86d7f5d3SJohn Marino 	/*
1001*86d7f5d3SJohn Marino 	 * Brightness is treated as an unsigned value by the decoder.
1002*86d7f5d3SJohn Marino 	 * 0 = dark, 128 = ITU level, 255 = bright
1003*86d7f5d3SJohn Marino 	 */
1004*86d7f5d3SJohn Marino 	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0a,
1005*86d7f5d3SJohn Marino 			     &brightness, sizeof(brightness))
1006*86d7f5d3SJohn Marino 	    != sizeof(brightness))
1007*86d7f5d3SJohn Marino 		return -1;
1008*86d7f5d3SJohn Marino 
1009*86d7f5d3SJohn Marino 	return brightness;
1010*86d7f5d3SJohn Marino }
1011*86d7f5d3SJohn Marino 
1012*86d7f5d3SJohn Marino 
1013*86d7f5d3SJohn Marino int
cxm_saa7115_set_brightness(struct cxm_softc * sc,unsigned char brightness)1014*86d7f5d3SJohn Marino cxm_saa7115_set_brightness(struct cxm_softc *sc, unsigned char brightness)
1015*86d7f5d3SJohn Marino {
1016*86d7f5d3SJohn Marino 
1017*86d7f5d3SJohn Marino 	/*
1018*86d7f5d3SJohn Marino 	 * Brightness is treated as an unsigned value by the decoder.
1019*86d7f5d3SJohn Marino 	 * 0 = dark, 128 = ITU level, 255 = bright
1020*86d7f5d3SJohn Marino 	 */
1021*86d7f5d3SJohn Marino 	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0a,
1022*86d7f5d3SJohn Marino 			      &brightness, sizeof(brightness))
1023*86d7f5d3SJohn Marino 	    != sizeof(brightness))
1024*86d7f5d3SJohn Marino 		return -1;
1025*86d7f5d3SJohn Marino 
1026*86d7f5d3SJohn Marino 	return 0;
1027*86d7f5d3SJohn Marino }
1028*86d7f5d3SJohn Marino 
1029*86d7f5d3SJohn Marino 
1030*86d7f5d3SJohn Marino int
cxm_saa7115_get_chroma_saturation(struct cxm_softc * sc)1031*86d7f5d3SJohn Marino cxm_saa7115_get_chroma_saturation(struct cxm_softc *sc)
1032*86d7f5d3SJohn Marino {
1033*86d7f5d3SJohn Marino 	unsigned char chroma_saturation;
1034*86d7f5d3SJohn Marino 
1035*86d7f5d3SJohn Marino 	/*
1036*86d7f5d3SJohn Marino 	 * Chroma saturation is treated as a signed value by the decoder.
1037*86d7f5d3SJohn Marino 	 * -128 = -2.0 (inverse chrominance), -64 = 1.0 (inverse chrominance),
1038*86d7f5d3SJohn Marino 	 * 0 = 0 (color off), 64 = 1.0 (ITU level), 127 = 1.984 (maximum)
1039*86d7f5d3SJohn Marino 	 */
1040*86d7f5d3SJohn Marino 	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0c,
1041*86d7f5d3SJohn Marino 			     &chroma_saturation, sizeof(chroma_saturation))
1042*86d7f5d3SJohn Marino 	    != sizeof(chroma_saturation))
1043*86d7f5d3SJohn Marino 		return -1;
1044*86d7f5d3SJohn Marino 
1045*86d7f5d3SJohn Marino 	return chroma_saturation;
1046*86d7f5d3SJohn Marino }
1047*86d7f5d3SJohn Marino 
1048*86d7f5d3SJohn Marino 
1049*86d7f5d3SJohn Marino int
cxm_saa7115_set_chroma_saturation(struct cxm_softc * sc,unsigned char chroma_saturation)1050*86d7f5d3SJohn Marino cxm_saa7115_set_chroma_saturation(struct cxm_softc *sc,
1051*86d7f5d3SJohn Marino 				   unsigned char chroma_saturation)
1052*86d7f5d3SJohn Marino {
1053*86d7f5d3SJohn Marino 
1054*86d7f5d3SJohn Marino 	/*
1055*86d7f5d3SJohn Marino 	 * Chroma saturation is treated as a signed value by the decoder.
1056*86d7f5d3SJohn Marino 	 * -128 = -2.0 (inverse chrominance), -64 = 1.0 (inverse chrominance),
1057*86d7f5d3SJohn Marino 	 * 0 = 0 (color off), 64 = 1.0 (ITU level), 127 = 1.984 (maximum)
1058*86d7f5d3SJohn Marino 	 */
1059*86d7f5d3SJohn Marino 	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0c,
1060*86d7f5d3SJohn Marino 			      &chroma_saturation, sizeof(chroma_saturation))
1061*86d7f5d3SJohn Marino 	    != sizeof(chroma_saturation))
1062*86d7f5d3SJohn Marino 		return -1;
1063*86d7f5d3SJohn Marino 
1064*86d7f5d3SJohn Marino 	return 0;
1065*86d7f5d3SJohn Marino }
1066*86d7f5d3SJohn Marino 
1067*86d7f5d3SJohn Marino 
1068*86d7f5d3SJohn Marino int
cxm_saa7115_get_contrast(struct cxm_softc * sc)1069*86d7f5d3SJohn Marino cxm_saa7115_get_contrast(struct cxm_softc *sc)
1070*86d7f5d3SJohn Marino {
1071*86d7f5d3SJohn Marino 	unsigned char contrast;
1072*86d7f5d3SJohn Marino 
1073*86d7f5d3SJohn Marino 	/*
1074*86d7f5d3SJohn Marino 	 * Contrast is treated as a signed value by the decoder.
1075*86d7f5d3SJohn Marino 	 * -128 = -2.0 (inverse luminance), -64 = 1.0 (inverse luminance),
1076*86d7f5d3SJohn Marino 	 * 0 = 0 (luminance off), 64 = 1.0, 68 = 1.063 (ITU level),
1077*86d7f5d3SJohn Marino 	 * 127 = 1.984 (maximum)
1078*86d7f5d3SJohn Marino 	 */
1079*86d7f5d3SJohn Marino 	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0b,
1080*86d7f5d3SJohn Marino 			     &contrast, sizeof(contrast)) != sizeof(contrast))
1081*86d7f5d3SJohn Marino 		return -1;
1082*86d7f5d3SJohn Marino 
1083*86d7f5d3SJohn Marino 	return contrast;
1084*86d7f5d3SJohn Marino }
1085*86d7f5d3SJohn Marino 
1086*86d7f5d3SJohn Marino 
1087*86d7f5d3SJohn Marino int
cxm_saa7115_set_contrast(struct cxm_softc * sc,unsigned char contrast)1088*86d7f5d3SJohn Marino cxm_saa7115_set_contrast(struct cxm_softc *sc, unsigned char contrast)
1089*86d7f5d3SJohn Marino {
1090*86d7f5d3SJohn Marino 
1091*86d7f5d3SJohn Marino 	/*
1092*86d7f5d3SJohn Marino 	 * Contrast is treated as a signed value by the decoder.
1093*86d7f5d3SJohn Marino 	 * -128 = -2.0 (inverse luminance), -64 = 1.0 (inverse luminance),
1094*86d7f5d3SJohn Marino 	 * 0 = 0 (luminance off), 64 = 1.0, 68 = 1.063 (ITU level),
1095*86d7f5d3SJohn Marino 	 * 127 = 1.984 (maximum)
1096*86d7f5d3SJohn Marino 	 */
1097*86d7f5d3SJohn Marino 	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0b,
1098*86d7f5d3SJohn Marino 			      &contrast, sizeof(contrast)) != sizeof(contrast))
1099*86d7f5d3SJohn Marino 		return -1;
1100*86d7f5d3SJohn Marino 
1101*86d7f5d3SJohn Marino 	return 0;
1102*86d7f5d3SJohn Marino }
1103*86d7f5d3SJohn Marino 
1104*86d7f5d3SJohn Marino 
1105*86d7f5d3SJohn Marino int
cxm_saa7115_get_hue(struct cxm_softc * sc)1106*86d7f5d3SJohn Marino cxm_saa7115_get_hue(struct cxm_softc *sc)
1107*86d7f5d3SJohn Marino {
1108*86d7f5d3SJohn Marino 	unsigned char hue;
1109*86d7f5d3SJohn Marino 
1110*86d7f5d3SJohn Marino 	/*
1111*86d7f5d3SJohn Marino 	 * Hue is treated as a signed value by the decoder.
1112*86d7f5d3SJohn Marino 	 * -128 = -180.0, 0 = 0.0, 127 = +178.6
1113*86d7f5d3SJohn Marino 	 */
1114*86d7f5d3SJohn Marino 	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0d,
1115*86d7f5d3SJohn Marino 			     &hue, sizeof(hue))
1116*86d7f5d3SJohn Marino 	    != sizeof(hue))
1117*86d7f5d3SJohn Marino 		return -1;
1118*86d7f5d3SJohn Marino 
1119*86d7f5d3SJohn Marino 	return hue;
1120*86d7f5d3SJohn Marino }
1121*86d7f5d3SJohn Marino 
1122*86d7f5d3SJohn Marino 
1123*86d7f5d3SJohn Marino int
cxm_saa7115_set_hue(struct cxm_softc * sc,unsigned char hue)1124*86d7f5d3SJohn Marino cxm_saa7115_set_hue(struct cxm_softc *sc, unsigned char hue)
1125*86d7f5d3SJohn Marino {
1126*86d7f5d3SJohn Marino 
1127*86d7f5d3SJohn Marino 	/*
1128*86d7f5d3SJohn Marino 	 * Hue is treated as a signed value by the decoder.
1129*86d7f5d3SJohn Marino 	 * -128 = -180.0, 0 = 0.0, 127 = +178.6
1130*86d7f5d3SJohn Marino 	 */
1131*86d7f5d3SJohn Marino 	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0d,
1132*86d7f5d3SJohn Marino 			      &hue, sizeof(hue))
1133*86d7f5d3SJohn Marino 	    != sizeof(hue))
1134*86d7f5d3SJohn Marino 		return -1;
1135*86d7f5d3SJohn Marino 
1136*86d7f5d3SJohn Marino 	return 0;
1137*86d7f5d3SJohn Marino }
1138*86d7f5d3SJohn Marino 
1139*86d7f5d3SJohn Marino 
1140*86d7f5d3SJohn Marino int
cxm_saa7115_is_locked(struct cxm_softc * sc)1141*86d7f5d3SJohn Marino cxm_saa7115_is_locked(struct cxm_softc *sc)
1142*86d7f5d3SJohn Marino {
1143*86d7f5d3SJohn Marino 	unsigned char status[1];
1144*86d7f5d3SJohn Marino 
1145*86d7f5d3SJohn Marino 	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x1f,
1146*86d7f5d3SJohn Marino 			     status, sizeof(status)) != sizeof(status))
1147*86d7f5d3SJohn Marino 		return -1;
1148*86d7f5d3SJohn Marino 
1149*86d7f5d3SJohn Marino 	return (status[0] & 0x01) ? 1 : 0;
1150*86d7f5d3SJohn Marino }
1151*86d7f5d3SJohn Marino 
1152*86d7f5d3SJohn Marino 
1153*86d7f5d3SJohn Marino int
cxm_saa7115_wait_for_lock(struct cxm_softc * sc)1154*86d7f5d3SJohn Marino cxm_saa7115_wait_for_lock(struct cxm_softc *sc)
1155*86d7f5d3SJohn Marino {
1156*86d7f5d3SJohn Marino 	unsigned int i;
1157*86d7f5d3SJohn Marino 
1158*86d7f5d3SJohn Marino 	/*
1159*86d7f5d3SJohn Marino 	 * Section 2.7 of the data sheet states:
1160*86d7f5d3SJohn Marino 	 *
1161*86d7f5d3SJohn Marino 	 *   Ultra-fast frame lock (almost 1 field)
1162*86d7f5d3SJohn Marino 	 *
1163*86d7f5d3SJohn Marino 	 * so hopefully 500 ms is enough (the lock
1164*86d7f5d3SJohn Marino 	 * sometimes takes a long time to occur ...
1165*86d7f5d3SJohn Marino 	 * possibly due to the time it takes to
1166*86d7f5d3SJohn Marino 	 * autodetect the format).
1167*86d7f5d3SJohn Marino 	 */
1168*86d7f5d3SJohn Marino 
1169*86d7f5d3SJohn Marino 	for (i = 0; i < 10; i++) {
1170*86d7f5d3SJohn Marino 
1171*86d7f5d3SJohn Marino 		/*
1172*86d7f5d3SJohn Marino 		 * The input may have just changed (prior to
1173*86d7f5d3SJohn Marino 		 * cxm_saa7115_wait_for_lock) so start with
1174*86d7f5d3SJohn Marino 		 * the delay to give the video decoder a
1175*86d7f5d3SJohn Marino 		 * chance to update its status.
1176*86d7f5d3SJohn Marino 		 */
1177*86d7f5d3SJohn Marino 
1178*86d7f5d3SJohn Marino 		tsleep(&sc->iicbus, 0, "video", hz / 20);
1179*86d7f5d3SJohn Marino 
1180*86d7f5d3SJohn Marino 		switch (cxm_saa7115_is_locked(sc)) {
1181*86d7f5d3SJohn Marino 		case 1:
1182*86d7f5d3SJohn Marino 			return 1;
1183*86d7f5d3SJohn Marino 
1184*86d7f5d3SJohn Marino 		case 0:
1185*86d7f5d3SJohn Marino 			break;
1186*86d7f5d3SJohn Marino 
1187*86d7f5d3SJohn Marino 		default:
1188*86d7f5d3SJohn Marino 			return -1;
1189*86d7f5d3SJohn Marino 		}
1190*86d7f5d3SJohn Marino 	}
1191*86d7f5d3SJohn Marino 
1192*86d7f5d3SJohn Marino 	device_printf(sc->dev, "video decoder failed to lock\n");
1193*86d7f5d3SJohn Marino 
1194*86d7f5d3SJohn Marino 	return 0;
1195*86d7f5d3SJohn Marino }
1196