xref: /openbmc/linux/drivers/media/pci/ivtv/ivtv-yuv.c (revision b285192a43f0432d82c2c10974204e78af0da596)
1*b285192aSMauro Carvalho Chehab /*
2*b285192aSMauro Carvalho Chehab     yuv support
3*b285192aSMauro Carvalho Chehab 
4*b285192aSMauro Carvalho Chehab     Copyright (C) 2007  Ian Armstrong <ian@iarmst.demon.co.uk>
5*b285192aSMauro Carvalho Chehab 
6*b285192aSMauro Carvalho Chehab     This program is free software; you can redistribute it and/or modify
7*b285192aSMauro Carvalho Chehab     it under the terms of the GNU General Public License as published by
8*b285192aSMauro Carvalho Chehab     the Free Software Foundation; either version 2 of the License, or
9*b285192aSMauro Carvalho Chehab     (at your option) any later version.
10*b285192aSMauro Carvalho Chehab 
11*b285192aSMauro Carvalho Chehab     This program is distributed in the hope that it will be useful,
12*b285192aSMauro Carvalho Chehab     but WITHOUT ANY WARRANTY; without even the implied warranty of
13*b285192aSMauro Carvalho Chehab     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*b285192aSMauro Carvalho Chehab     GNU General Public License for more details.
15*b285192aSMauro Carvalho Chehab 
16*b285192aSMauro Carvalho Chehab     You should have received a copy of the GNU General Public License
17*b285192aSMauro Carvalho Chehab     along with this program; if not, write to the Free Software
18*b285192aSMauro Carvalho Chehab     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19*b285192aSMauro Carvalho Chehab  */
20*b285192aSMauro Carvalho Chehab 
21*b285192aSMauro Carvalho Chehab #include "ivtv-driver.h"
22*b285192aSMauro Carvalho Chehab #include "ivtv-udma.h"
23*b285192aSMauro Carvalho Chehab #include "ivtv-yuv.h"
24*b285192aSMauro Carvalho Chehab 
25*b285192aSMauro Carvalho Chehab /* YUV buffer offsets */
26*b285192aSMauro Carvalho Chehab const u32 yuv_offset[IVTV_YUV_BUFFERS] = {
27*b285192aSMauro Carvalho Chehab 	0x001a8600,
28*b285192aSMauro Carvalho Chehab 	0x00240400,
29*b285192aSMauro Carvalho Chehab 	0x002d8200,
30*b285192aSMauro Carvalho Chehab 	0x00370000,
31*b285192aSMauro Carvalho Chehab 	0x00029000,
32*b285192aSMauro Carvalho Chehab 	0x000C0E00,
33*b285192aSMauro Carvalho Chehab 	0x006B0400,
34*b285192aSMauro Carvalho Chehab 	0x00748200
35*b285192aSMauro Carvalho Chehab };
36*b285192aSMauro Carvalho Chehab 
37*b285192aSMauro Carvalho Chehab static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
38*b285192aSMauro Carvalho Chehab 				  struct ivtv_dma_frame *args)
39*b285192aSMauro Carvalho Chehab {
40*b285192aSMauro Carvalho Chehab 	struct ivtv_dma_page_info y_dma;
41*b285192aSMauro Carvalho Chehab 	struct ivtv_dma_page_info uv_dma;
42*b285192aSMauro Carvalho Chehab 	struct yuv_playback_info *yi = &itv->yuv_info;
43*b285192aSMauro Carvalho Chehab 	u8 frame = yi->draw_frame;
44*b285192aSMauro Carvalho Chehab 	struct yuv_frame_info *f = &yi->new_frame_info[frame];
45*b285192aSMauro Carvalho Chehab 	int i;
46*b285192aSMauro Carvalho Chehab 	int y_pages, uv_pages;
47*b285192aSMauro Carvalho Chehab 	unsigned long y_buffer_offset, uv_buffer_offset;
48*b285192aSMauro Carvalho Chehab 	int y_decode_height, uv_decode_height, y_size;
49*b285192aSMauro Carvalho Chehab 
50*b285192aSMauro Carvalho Chehab 	y_buffer_offset = IVTV_DECODER_OFFSET + yuv_offset[frame];
51*b285192aSMauro Carvalho Chehab 	uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET;
52*b285192aSMauro Carvalho Chehab 
53*b285192aSMauro Carvalho Chehab 	y_decode_height = uv_decode_height = f->src_h + f->src_y;
54*b285192aSMauro Carvalho Chehab 
55*b285192aSMauro Carvalho Chehab 	if (f->offset_y)
56*b285192aSMauro Carvalho Chehab 		y_buffer_offset += 720 * 16;
57*b285192aSMauro Carvalho Chehab 
58*b285192aSMauro Carvalho Chehab 	if (y_decode_height & 15)
59*b285192aSMauro Carvalho Chehab 		y_decode_height = (y_decode_height + 16) & ~15;
60*b285192aSMauro Carvalho Chehab 
61*b285192aSMauro Carvalho Chehab 	if (uv_decode_height & 31)
62*b285192aSMauro Carvalho Chehab 		uv_decode_height = (uv_decode_height + 32) & ~31;
63*b285192aSMauro Carvalho Chehab 
64*b285192aSMauro Carvalho Chehab 	y_size = 720 * y_decode_height;
65*b285192aSMauro Carvalho Chehab 
66*b285192aSMauro Carvalho Chehab 	/* Still in USE */
67*b285192aSMauro Carvalho Chehab 	if (dma->SG_length || dma->page_count) {
68*b285192aSMauro Carvalho Chehab 		IVTV_DEBUG_WARN
69*b285192aSMauro Carvalho Chehab 		    ("prep_user_dma: SG_length %d page_count %d still full?\n",
70*b285192aSMauro Carvalho Chehab 		     dma->SG_length, dma->page_count);
71*b285192aSMauro Carvalho Chehab 		return -EBUSY;
72*b285192aSMauro Carvalho Chehab 	}
73*b285192aSMauro Carvalho Chehab 
74*b285192aSMauro Carvalho Chehab 	ivtv_udma_get_page_info (&y_dma, (unsigned long)args->y_source, 720 * y_decode_height);
75*b285192aSMauro Carvalho Chehab 	ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height);
76*b285192aSMauro Carvalho Chehab 
77*b285192aSMauro Carvalho Chehab 	/* Get user pages for DMA Xfer */
78*b285192aSMauro Carvalho Chehab 	down_read(&current->mm->mmap_sem);
79*b285192aSMauro Carvalho Chehab 	y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL);
80*b285192aSMauro Carvalho Chehab 	uv_pages = 0; /* silence gcc. value is set and consumed only if: */
81*b285192aSMauro Carvalho Chehab 	if (y_pages == y_dma.page_count) {
82*b285192aSMauro Carvalho Chehab 		uv_pages = get_user_pages(current, current->mm,
83*b285192aSMauro Carvalho Chehab 					  uv_dma.uaddr, uv_dma.page_count, 0, 1,
84*b285192aSMauro Carvalho Chehab 					  &dma->map[y_pages], NULL);
85*b285192aSMauro Carvalho Chehab 	}
86*b285192aSMauro Carvalho Chehab 	up_read(&current->mm->mmap_sem);
87*b285192aSMauro Carvalho Chehab 
88*b285192aSMauro Carvalho Chehab 	if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) {
89*b285192aSMauro Carvalho Chehab 		int rc = -EFAULT;
90*b285192aSMauro Carvalho Chehab 
91*b285192aSMauro Carvalho Chehab 		if (y_pages == y_dma.page_count) {
92*b285192aSMauro Carvalho Chehab 			IVTV_DEBUG_WARN
93*b285192aSMauro Carvalho Chehab 				("failed to map uv user pages, returned %d "
94*b285192aSMauro Carvalho Chehab 				 "expecting %d\n", uv_pages, uv_dma.page_count);
95*b285192aSMauro Carvalho Chehab 
96*b285192aSMauro Carvalho Chehab 			if (uv_pages >= 0) {
97*b285192aSMauro Carvalho Chehab 				for (i = 0; i < uv_pages; i++)
98*b285192aSMauro Carvalho Chehab 					put_page(dma->map[y_pages + i]);
99*b285192aSMauro Carvalho Chehab 				rc = -EFAULT;
100*b285192aSMauro Carvalho Chehab 			} else {
101*b285192aSMauro Carvalho Chehab 				rc = uv_pages;
102*b285192aSMauro Carvalho Chehab 			}
103*b285192aSMauro Carvalho Chehab 		} else {
104*b285192aSMauro Carvalho Chehab 			IVTV_DEBUG_WARN
105*b285192aSMauro Carvalho Chehab 				("failed to map y user pages, returned %d "
106*b285192aSMauro Carvalho Chehab 				 "expecting %d\n", y_pages, y_dma.page_count);
107*b285192aSMauro Carvalho Chehab 		}
108*b285192aSMauro Carvalho Chehab 		if (y_pages >= 0) {
109*b285192aSMauro Carvalho Chehab 			for (i = 0; i < y_pages; i++)
110*b285192aSMauro Carvalho Chehab 				put_page(dma->map[i]);
111*b285192aSMauro Carvalho Chehab 			/*
112*b285192aSMauro Carvalho Chehab 			 * Inherit the -EFAULT from rc's
113*b285192aSMauro Carvalho Chehab 			 * initialization, but allow it to be
114*b285192aSMauro Carvalho Chehab 			 * overriden by uv_pages above if it was an
115*b285192aSMauro Carvalho Chehab 			 * actual errno.
116*b285192aSMauro Carvalho Chehab 			 */
117*b285192aSMauro Carvalho Chehab 		} else {
118*b285192aSMauro Carvalho Chehab 			rc = y_pages;
119*b285192aSMauro Carvalho Chehab 		}
120*b285192aSMauro Carvalho Chehab 		return rc;
121*b285192aSMauro Carvalho Chehab 	}
122*b285192aSMauro Carvalho Chehab 
123*b285192aSMauro Carvalho Chehab 	dma->page_count = y_pages + uv_pages;
124*b285192aSMauro Carvalho Chehab 
125*b285192aSMauro Carvalho Chehab 	/* Fill & map SG List */
126*b285192aSMauro Carvalho Chehab 	if (ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0)) < 0) {
127*b285192aSMauro Carvalho Chehab 		IVTV_DEBUG_WARN("could not allocate bounce buffers for highmem userspace buffers\n");
128*b285192aSMauro Carvalho Chehab 		for (i = 0; i < dma->page_count; i++) {
129*b285192aSMauro Carvalho Chehab 			put_page(dma->map[i]);
130*b285192aSMauro Carvalho Chehab 		}
131*b285192aSMauro Carvalho Chehab 		dma->page_count = 0;
132*b285192aSMauro Carvalho Chehab 		return -ENOMEM;
133*b285192aSMauro Carvalho Chehab 	}
134*b285192aSMauro Carvalho Chehab 	dma->SG_length = pci_map_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
135*b285192aSMauro Carvalho Chehab 
136*b285192aSMauro Carvalho Chehab 	/* Fill SG Array with new values */
137*b285192aSMauro Carvalho Chehab 	ivtv_udma_fill_sg_array(dma, y_buffer_offset, uv_buffer_offset, y_size);
138*b285192aSMauro Carvalho Chehab 
139*b285192aSMauro Carvalho Chehab 	/* If we've offset the y plane, ensure top area is blanked */
140*b285192aSMauro Carvalho Chehab 	if (f->offset_y && yi->blanking_dmaptr) {
141*b285192aSMauro Carvalho Chehab 		dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16);
142*b285192aSMauro Carvalho Chehab 		dma->SGarray[dma->SG_length].src = cpu_to_le32(yi->blanking_dmaptr);
143*b285192aSMauro Carvalho Chehab 		dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DECODER_OFFSET + yuv_offset[frame]);
144*b285192aSMauro Carvalho Chehab 		dma->SG_length++;
145*b285192aSMauro Carvalho Chehab 	}
146*b285192aSMauro Carvalho Chehab 
147*b285192aSMauro Carvalho Chehab 	/* Tag SG Array with Interrupt Bit */
148*b285192aSMauro Carvalho Chehab 	dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
149*b285192aSMauro Carvalho Chehab 
150*b285192aSMauro Carvalho Chehab 	ivtv_udma_sync_for_device(itv);
151*b285192aSMauro Carvalho Chehab 	return 0;
152*b285192aSMauro Carvalho Chehab }
153*b285192aSMauro Carvalho Chehab 
154*b285192aSMauro Carvalho Chehab /* We rely on a table held in the firmware - Quick check. */
155*b285192aSMauro Carvalho Chehab int ivtv_yuv_filter_check(struct ivtv *itv)
156*b285192aSMauro Carvalho Chehab {
157*b285192aSMauro Carvalho Chehab 	int i, y, uv;
158*b285192aSMauro Carvalho Chehab 
159*b285192aSMauro Carvalho Chehab 	for (i = 0, y = 16, uv = 4; i < 16; i++, y += 24, uv += 12) {
160*b285192aSMauro Carvalho Chehab 		if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + y) != i << 16) ||
161*b285192aSMauro Carvalho Chehab 		    (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + uv) != i << 16)) {
162*b285192aSMauro Carvalho Chehab 			IVTV_WARN ("YUV filter table not found in firmware.\n");
163*b285192aSMauro Carvalho Chehab 			return -1;
164*b285192aSMauro Carvalho Chehab 		}
165*b285192aSMauro Carvalho Chehab 	}
166*b285192aSMauro Carvalho Chehab 	return 0;
167*b285192aSMauro Carvalho Chehab }
168*b285192aSMauro Carvalho Chehab 
169*b285192aSMauro Carvalho Chehab static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2)
170*b285192aSMauro Carvalho Chehab {
171*b285192aSMauro Carvalho Chehab 	u32 i, line;
172*b285192aSMauro Carvalho Chehab 
173*b285192aSMauro Carvalho Chehab 	/* If any filter is -1, then don't update it */
174*b285192aSMauro Carvalho Chehab 	if (h_filter > -1) {
175*b285192aSMauro Carvalho Chehab 		if (h_filter > 4)
176*b285192aSMauro Carvalho Chehab 			h_filter = 4;
177*b285192aSMauro Carvalho Chehab 		i = IVTV_YUV_HORIZONTAL_FILTER_OFFSET + (h_filter * 384);
178*b285192aSMauro Carvalho Chehab 		for (line = 0; line < 16; line++) {
179*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x02804);
180*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x0281c);
181*b285192aSMauro Carvalho Chehab 			i += 4;
182*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x02808);
183*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x02820);
184*b285192aSMauro Carvalho Chehab 			i += 4;
185*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x0280c);
186*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x02824);
187*b285192aSMauro Carvalho Chehab 			i += 4;
188*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x02810);
189*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x02828);
190*b285192aSMauro Carvalho Chehab 			i += 4;
191*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x02814);
192*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x0282c);
193*b285192aSMauro Carvalho Chehab 			i += 8;
194*b285192aSMauro Carvalho Chehab 			write_reg(0, 0x02818);
195*b285192aSMauro Carvalho Chehab 			write_reg(0, 0x02830);
196*b285192aSMauro Carvalho Chehab 		}
197*b285192aSMauro Carvalho Chehab 		IVTV_DEBUG_YUV("h_filter -> %d\n", h_filter);
198*b285192aSMauro Carvalho Chehab 	}
199*b285192aSMauro Carvalho Chehab 
200*b285192aSMauro Carvalho Chehab 	if (v_filter_1 > -1) {
201*b285192aSMauro Carvalho Chehab 		if (v_filter_1 > 4)
202*b285192aSMauro Carvalho Chehab 			v_filter_1 = 4;
203*b285192aSMauro Carvalho Chehab 		i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_1 * 192);
204*b285192aSMauro Carvalho Chehab 		for (line = 0; line < 16; line++) {
205*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x02900);
206*b285192aSMauro Carvalho Chehab 			i += 4;
207*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x02904);
208*b285192aSMauro Carvalho Chehab 			i += 8;
209*b285192aSMauro Carvalho Chehab 			write_reg(0, 0x02908);
210*b285192aSMauro Carvalho Chehab 		}
211*b285192aSMauro Carvalho Chehab 		IVTV_DEBUG_YUV("v_filter_1 -> %d\n", v_filter_1);
212*b285192aSMauro Carvalho Chehab 	}
213*b285192aSMauro Carvalho Chehab 
214*b285192aSMauro Carvalho Chehab 	if (v_filter_2 > -1) {
215*b285192aSMauro Carvalho Chehab 		if (v_filter_2 > 4)
216*b285192aSMauro Carvalho Chehab 			v_filter_2 = 4;
217*b285192aSMauro Carvalho Chehab 		i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_2 * 192);
218*b285192aSMauro Carvalho Chehab 		for (line = 0; line < 16; line++) {
219*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x0290c);
220*b285192aSMauro Carvalho Chehab 			i += 4;
221*b285192aSMauro Carvalho Chehab 			write_reg(read_dec(i), 0x02910);
222*b285192aSMauro Carvalho Chehab 			i += 8;
223*b285192aSMauro Carvalho Chehab 			write_reg(0, 0x02914);
224*b285192aSMauro Carvalho Chehab 		}
225*b285192aSMauro Carvalho Chehab 		IVTV_DEBUG_YUV("v_filter_2 -> %d\n", v_filter_2);
226*b285192aSMauro Carvalho Chehab 	}
227*b285192aSMauro Carvalho Chehab }
228*b285192aSMauro Carvalho Chehab 
229*b285192aSMauro Carvalho Chehab static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *f)
230*b285192aSMauro Carvalho Chehab {
231*b285192aSMauro Carvalho Chehab 	struct yuv_playback_info *yi = &itv->yuv_info;
232*b285192aSMauro Carvalho Chehab 	u32 reg_2834, reg_2838, reg_283c;
233*b285192aSMauro Carvalho Chehab 	u32 reg_2844, reg_2854, reg_285c;
234*b285192aSMauro Carvalho Chehab 	u32 reg_2864, reg_2874, reg_2890;
235*b285192aSMauro Carvalho Chehab 	u32 reg_2870, reg_2870_base, reg_2870_offset;
236*b285192aSMauro Carvalho Chehab 	int x_cutoff;
237*b285192aSMauro Carvalho Chehab 	int h_filter;
238*b285192aSMauro Carvalho Chehab 	u32 master_width;
239*b285192aSMauro Carvalho Chehab 
240*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_WARN
241*b285192aSMauro Carvalho Chehab 	    ("Adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n",
242*b285192aSMauro Carvalho Chehab 	     f->tru_w, f->src_w, f->dst_w, f->src_x, f->dst_x);
243*b285192aSMauro Carvalho Chehab 
244*b285192aSMauro Carvalho Chehab 	/* How wide is the src image */
245*b285192aSMauro Carvalho Chehab 	x_cutoff = f->src_w + f->src_x;
246*b285192aSMauro Carvalho Chehab 
247*b285192aSMauro Carvalho Chehab 	/* Set the display width */
248*b285192aSMauro Carvalho Chehab 	reg_2834 = f->dst_w;
249*b285192aSMauro Carvalho Chehab 	reg_2838 = reg_2834;
250*b285192aSMauro Carvalho Chehab 
251*b285192aSMauro Carvalho Chehab 	/* Set the display position */
252*b285192aSMauro Carvalho Chehab 	reg_2890 = f->dst_x;
253*b285192aSMauro Carvalho Chehab 
254*b285192aSMauro Carvalho Chehab 	/* Index into the image horizontally */
255*b285192aSMauro Carvalho Chehab 	reg_2870 = 0;
256*b285192aSMauro Carvalho Chehab 
257*b285192aSMauro Carvalho Chehab 	/* 2870 is normally fudged to align video coords with osd coords.
258*b285192aSMauro Carvalho Chehab 	   If running full screen, it causes an unwanted left shift
259*b285192aSMauro Carvalho Chehab 	   Remove the fudge if we almost fill the screen.
260*b285192aSMauro Carvalho Chehab 	   Gradually adjust the offset to avoid the video 'snapping'
261*b285192aSMauro Carvalho Chehab 	   left/right if it gets dragged through this region.
262*b285192aSMauro Carvalho Chehab 	   Only do this if osd is full width. */
263*b285192aSMauro Carvalho Chehab 	if (f->vis_w == 720) {
264*b285192aSMauro Carvalho Chehab 		if ((f->tru_x - f->pan_x > -1) && (f->tru_x - f->pan_x <= 40) && (f->dst_w >= 680))
265*b285192aSMauro Carvalho Chehab 			reg_2870 = 10 - (f->tru_x - f->pan_x) / 4;
266*b285192aSMauro Carvalho Chehab 		else if ((f->tru_x - f->pan_x < 0) && (f->tru_x - f->pan_x >= -20) && (f->dst_w >= 660))
267*b285192aSMauro Carvalho Chehab 			reg_2870 = (10 + (f->tru_x - f->pan_x) / 2);
268*b285192aSMauro Carvalho Chehab 
269*b285192aSMauro Carvalho Chehab 		if (f->dst_w >= f->src_w)
270*b285192aSMauro Carvalho Chehab 			reg_2870 = reg_2870 << 16 | reg_2870;
271*b285192aSMauro Carvalho Chehab 		else
272*b285192aSMauro Carvalho Chehab 			reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1);
273*b285192aSMauro Carvalho Chehab 	}
274*b285192aSMauro Carvalho Chehab 
275*b285192aSMauro Carvalho Chehab 	if (f->dst_w < f->src_w)
276*b285192aSMauro Carvalho Chehab 		reg_2870 = 0x000d000e - reg_2870;
277*b285192aSMauro Carvalho Chehab 	else
278*b285192aSMauro Carvalho Chehab 		reg_2870 = 0x0012000e - reg_2870;
279*b285192aSMauro Carvalho Chehab 
280*b285192aSMauro Carvalho Chehab 	/* We're also using 2870 to shift the image left (src_x & negative dst_x) */
281*b285192aSMauro Carvalho Chehab 	reg_2870_offset = (f->src_x * ((f->dst_w << 21) / f->src_w)) >> 19;
282*b285192aSMauro Carvalho Chehab 
283*b285192aSMauro Carvalho Chehab 	if (f->dst_w >= f->src_w) {
284*b285192aSMauro Carvalho Chehab 		x_cutoff &= ~1;
285*b285192aSMauro Carvalho Chehab 		master_width = (f->src_w * 0x00200000) / (f->dst_w);
286*b285192aSMauro Carvalho Chehab 		if (master_width * f->dst_w != f->src_w * 0x00200000)
287*b285192aSMauro Carvalho Chehab 			master_width++;
288*b285192aSMauro Carvalho Chehab 		reg_2834 = (reg_2834 << 16) | x_cutoff;
289*b285192aSMauro Carvalho Chehab 		reg_2838 = (reg_2838 << 16) | x_cutoff;
290*b285192aSMauro Carvalho Chehab 		reg_283c = master_width >> 2;
291*b285192aSMauro Carvalho Chehab 		reg_2844 = master_width >> 2;
292*b285192aSMauro Carvalho Chehab 		reg_2854 = master_width;
293*b285192aSMauro Carvalho Chehab 		reg_285c = master_width >> 1;
294*b285192aSMauro Carvalho Chehab 		reg_2864 = master_width >> 1;
295*b285192aSMauro Carvalho Chehab 
296*b285192aSMauro Carvalho Chehab 		/* We also need to factor in the scaling
297*b285192aSMauro Carvalho Chehab 		   (src_w - dst_w) / (src_w / 4) */
298*b285192aSMauro Carvalho Chehab 		if (f->dst_w > f->src_w)
299*b285192aSMauro Carvalho Chehab 			reg_2870_base = ((f->dst_w - f->src_w)<<16) / (f->src_w <<14);
300*b285192aSMauro Carvalho Chehab 		else
301*b285192aSMauro Carvalho Chehab 			reg_2870_base = 0;
302*b285192aSMauro Carvalho Chehab 
303*b285192aSMauro Carvalho Chehab 		reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base);
304*b285192aSMauro Carvalho Chehab 		reg_2874 = 0;
305*b285192aSMauro Carvalho Chehab 	} else if (f->dst_w < f->src_w / 2) {
306*b285192aSMauro Carvalho Chehab 		master_width = (f->src_w * 0x00080000) / f->dst_w;
307*b285192aSMauro Carvalho Chehab 		if (master_width * f->dst_w != f->src_w * 0x00080000)
308*b285192aSMauro Carvalho Chehab 			master_width++;
309*b285192aSMauro Carvalho Chehab 		reg_2834 = (reg_2834 << 16) | x_cutoff;
310*b285192aSMauro Carvalho Chehab 		reg_2838 = (reg_2838 << 16) | x_cutoff;
311*b285192aSMauro Carvalho Chehab 		reg_283c = master_width >> 2;
312*b285192aSMauro Carvalho Chehab 		reg_2844 = master_width >> 1;
313*b285192aSMauro Carvalho Chehab 		reg_2854 = master_width;
314*b285192aSMauro Carvalho Chehab 		reg_285c = master_width >> 1;
315*b285192aSMauro Carvalho Chehab 		reg_2864 = master_width >> 1;
316*b285192aSMauro Carvalho Chehab 		reg_2870 += ((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset;
317*b285192aSMauro Carvalho Chehab 		reg_2870 += (5 - (((f->src_w + f->src_w / 2) - 1) / f->dst_w)) << 16;
318*b285192aSMauro Carvalho Chehab 		reg_2874 = 0x00000012;
319*b285192aSMauro Carvalho Chehab 	} else {
320*b285192aSMauro Carvalho Chehab 		master_width = (f->src_w * 0x00100000) / f->dst_w;
321*b285192aSMauro Carvalho Chehab 		if (master_width * f->dst_w != f->src_w * 0x00100000)
322*b285192aSMauro Carvalho Chehab 			master_width++;
323*b285192aSMauro Carvalho Chehab 		reg_2834 = (reg_2834 << 16) | x_cutoff;
324*b285192aSMauro Carvalho Chehab 		reg_2838 = (reg_2838 << 16) | x_cutoff;
325*b285192aSMauro Carvalho Chehab 		reg_283c = master_width >> 2;
326*b285192aSMauro Carvalho Chehab 		reg_2844 = master_width >> 1;
327*b285192aSMauro Carvalho Chehab 		reg_2854 = master_width;
328*b285192aSMauro Carvalho Chehab 		reg_285c = master_width >> 1;
329*b285192aSMauro Carvalho Chehab 		reg_2864 = master_width >> 1;
330*b285192aSMauro Carvalho Chehab 		reg_2870 += ((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1;
331*b285192aSMauro Carvalho Chehab 		reg_2870 += (5 - (((f->src_w * 3) - 1) / f->dst_w)) << 16;
332*b285192aSMauro Carvalho Chehab 		reg_2874 = 0x00000001;
333*b285192aSMauro Carvalho Chehab 	}
334*b285192aSMauro Carvalho Chehab 
335*b285192aSMauro Carvalho Chehab 	/* Select the horizontal filter */
336*b285192aSMauro Carvalho Chehab 	if (f->src_w == f->dst_w) {
337*b285192aSMauro Carvalho Chehab 		/* An exact size match uses filter 0 */
338*b285192aSMauro Carvalho Chehab 		h_filter = 0;
339*b285192aSMauro Carvalho Chehab 	} else {
340*b285192aSMauro Carvalho Chehab 		/* Figure out which filter to use */
341*b285192aSMauro Carvalho Chehab 		h_filter = ((f->src_w << 16) / f->dst_w) >> 15;
342*b285192aSMauro Carvalho Chehab 		h_filter = (h_filter >> 1) + (h_filter & 1);
343*b285192aSMauro Carvalho Chehab 		/* Only an exact size match can use filter 0 */
344*b285192aSMauro Carvalho Chehab 		h_filter += !h_filter;
345*b285192aSMauro Carvalho Chehab 	}
346*b285192aSMauro Carvalho Chehab 
347*b285192aSMauro Carvalho Chehab 	write_reg(reg_2834, 0x02834);
348*b285192aSMauro Carvalho Chehab 	write_reg(reg_2838, 0x02838);
349*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",
350*b285192aSMauro Carvalho Chehab 		       yi->reg_2834, reg_2834, yi->reg_2838, reg_2838);
351*b285192aSMauro Carvalho Chehab 
352*b285192aSMauro Carvalho Chehab 	write_reg(reg_283c, 0x0283c);
353*b285192aSMauro Carvalho Chehab 	write_reg(reg_2844, 0x02844);
354*b285192aSMauro Carvalho Chehab 
355*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",
356*b285192aSMauro Carvalho Chehab 		       yi->reg_283c, reg_283c, yi->reg_2844, reg_2844);
357*b285192aSMauro Carvalho Chehab 
358*b285192aSMauro Carvalho Chehab 	write_reg(0x00080514, 0x02840);
359*b285192aSMauro Carvalho Chehab 	write_reg(0x00100514, 0x02848);
360*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",
361*b285192aSMauro Carvalho Chehab 		       yi->reg_2840, 0x00080514, yi->reg_2848, 0x00100514);
362*b285192aSMauro Carvalho Chehab 
363*b285192aSMauro Carvalho Chehab 	write_reg(reg_2854, 0x02854);
364*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",
365*b285192aSMauro Carvalho Chehab 		       yi->reg_2854, reg_2854);
366*b285192aSMauro Carvalho Chehab 
367*b285192aSMauro Carvalho Chehab 	write_reg(reg_285c, 0x0285c);
368*b285192aSMauro Carvalho Chehab 	write_reg(reg_2864, 0x02864);
369*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",
370*b285192aSMauro Carvalho Chehab 		       yi->reg_285c, reg_285c, yi->reg_2864, reg_2864);
371*b285192aSMauro Carvalho Chehab 
372*b285192aSMauro Carvalho Chehab 	write_reg(reg_2874, 0x02874);
373*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",
374*b285192aSMauro Carvalho Chehab 		       yi->reg_2874, reg_2874);
375*b285192aSMauro Carvalho Chehab 
376*b285192aSMauro Carvalho Chehab 	write_reg(reg_2870, 0x02870);
377*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",
378*b285192aSMauro Carvalho Chehab 		       yi->reg_2870, reg_2870);
379*b285192aSMauro Carvalho Chehab 
380*b285192aSMauro Carvalho Chehab 	write_reg(reg_2890, 0x02890);
381*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",
382*b285192aSMauro Carvalho Chehab 		       yi->reg_2890, reg_2890);
383*b285192aSMauro Carvalho Chehab 
384*b285192aSMauro Carvalho Chehab 	/* Only update the filter if we really need to */
385*b285192aSMauro Carvalho Chehab 	if (h_filter != yi->h_filter) {
386*b285192aSMauro Carvalho Chehab 		ivtv_yuv_filter(itv, h_filter, -1, -1);
387*b285192aSMauro Carvalho Chehab 		yi->h_filter = h_filter;
388*b285192aSMauro Carvalho Chehab 	}
389*b285192aSMauro Carvalho Chehab }
390*b285192aSMauro Carvalho Chehab 
391*b285192aSMauro Carvalho Chehab static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *f)
392*b285192aSMauro Carvalho Chehab {
393*b285192aSMauro Carvalho Chehab 	struct yuv_playback_info *yi = &itv->yuv_info;
394*b285192aSMauro Carvalho Chehab 	u32 master_height;
395*b285192aSMauro Carvalho Chehab 	u32 reg_2918, reg_291c, reg_2920, reg_2928;
396*b285192aSMauro Carvalho Chehab 	u32 reg_2930, reg_2934, reg_293c;
397*b285192aSMauro Carvalho Chehab 	u32 reg_2940, reg_2944, reg_294c;
398*b285192aSMauro Carvalho Chehab 	u32 reg_2950, reg_2954, reg_2958, reg_295c;
399*b285192aSMauro Carvalho Chehab 	u32 reg_2960, reg_2964, reg_2968, reg_296c;
400*b285192aSMauro Carvalho Chehab 	u32 reg_289c;
401*b285192aSMauro Carvalho Chehab 	u32 src_major_y, src_minor_y;
402*b285192aSMauro Carvalho Chehab 	u32 src_major_uv, src_minor_uv;
403*b285192aSMauro Carvalho Chehab 	u32 reg_2964_base, reg_2968_base;
404*b285192aSMauro Carvalho Chehab 	int v_filter_1, v_filter_2;
405*b285192aSMauro Carvalho Chehab 
406*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_WARN
407*b285192aSMauro Carvalho Chehab 	    ("Adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n",
408*b285192aSMauro Carvalho Chehab 	     f->tru_h, f->src_h, f->dst_h, f->src_y, f->dst_y);
409*b285192aSMauro Carvalho Chehab 
410*b285192aSMauro Carvalho Chehab 	/* What scaling mode is being used... */
411*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Scaling mode Y: %s\n",
412*b285192aSMauro Carvalho Chehab 		       f->interlaced_y ? "Interlaced" : "Progressive");
413*b285192aSMauro Carvalho Chehab 
414*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Scaling mode UV: %s\n",
415*b285192aSMauro Carvalho Chehab 		       f->interlaced_uv ? "Interlaced" : "Progressive");
416*b285192aSMauro Carvalho Chehab 
417*b285192aSMauro Carvalho Chehab 	/* What is the source video being treated as... */
418*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_WARN("Source video: %s\n",
419*b285192aSMauro Carvalho Chehab 			f->interlaced ? "Interlaced" : "Progressive");
420*b285192aSMauro Carvalho Chehab 
421*b285192aSMauro Carvalho Chehab 	/* We offset into the image using two different index methods, so split
422*b285192aSMauro Carvalho Chehab 	   the y source coord into two parts. */
423*b285192aSMauro Carvalho Chehab 	if (f->src_y < 8) {
424*b285192aSMauro Carvalho Chehab 		src_minor_uv = f->src_y;
425*b285192aSMauro Carvalho Chehab 		src_major_uv = 0;
426*b285192aSMauro Carvalho Chehab 	} else {
427*b285192aSMauro Carvalho Chehab 		src_minor_uv = 8;
428*b285192aSMauro Carvalho Chehab 		src_major_uv = f->src_y - 8;
429*b285192aSMauro Carvalho Chehab 	}
430*b285192aSMauro Carvalho Chehab 
431*b285192aSMauro Carvalho Chehab 	src_minor_y = src_minor_uv;
432*b285192aSMauro Carvalho Chehab 	src_major_y = src_major_uv;
433*b285192aSMauro Carvalho Chehab 
434*b285192aSMauro Carvalho Chehab 	if (f->offset_y)
435*b285192aSMauro Carvalho Chehab 		src_minor_y += 16;
436*b285192aSMauro Carvalho Chehab 
437*b285192aSMauro Carvalho Chehab 	if (f->interlaced_y)
438*b285192aSMauro Carvalho Chehab 		reg_2918 = (f->dst_h << 16) | (f->src_h + src_minor_y);
439*b285192aSMauro Carvalho Chehab 	else
440*b285192aSMauro Carvalho Chehab 		reg_2918 = (f->dst_h << 16) | ((f->src_h + src_minor_y) << 1);
441*b285192aSMauro Carvalho Chehab 
442*b285192aSMauro Carvalho Chehab 	if (f->interlaced_uv)
443*b285192aSMauro Carvalho Chehab 		reg_291c = (f->dst_h << 16) | ((f->src_h + src_minor_uv) >> 1);
444*b285192aSMauro Carvalho Chehab 	else
445*b285192aSMauro Carvalho Chehab 		reg_291c = (f->dst_h << 16) | (f->src_h + src_minor_uv);
446*b285192aSMauro Carvalho Chehab 
447*b285192aSMauro Carvalho Chehab 	reg_2964_base = (src_minor_y * ((f->dst_h << 16) / f->src_h)) >> 14;
448*b285192aSMauro Carvalho Chehab 	reg_2968_base = (src_minor_uv * ((f->dst_h << 16) / f->src_h)) >> 14;
449*b285192aSMauro Carvalho Chehab 
450*b285192aSMauro Carvalho Chehab 	if (f->dst_h / 2 >= f->src_h && !f->interlaced_y) {
451*b285192aSMauro Carvalho Chehab 		master_height = (f->src_h * 0x00400000) / f->dst_h;
452*b285192aSMauro Carvalho Chehab 		if ((f->src_h * 0x00400000) - (master_height * f->dst_h) >= f->dst_h / 2)
453*b285192aSMauro Carvalho Chehab 			master_height++;
454*b285192aSMauro Carvalho Chehab 		reg_2920 = master_height >> 2;
455*b285192aSMauro Carvalho Chehab 		reg_2928 = master_height >> 3;
456*b285192aSMauro Carvalho Chehab 		reg_2930 = master_height;
457*b285192aSMauro Carvalho Chehab 		reg_2940 = master_height >> 1;
458*b285192aSMauro Carvalho Chehab 		reg_2964_base >>= 3;
459*b285192aSMauro Carvalho Chehab 		reg_2968_base >>= 3;
460*b285192aSMauro Carvalho Chehab 		reg_296c = 0x00000000;
461*b285192aSMauro Carvalho Chehab 	} else if (f->dst_h >= f->src_h) {
462*b285192aSMauro Carvalho Chehab 		master_height = (f->src_h * 0x00400000) / f->dst_h;
463*b285192aSMauro Carvalho Chehab 		master_height = (master_height >> 1) + (master_height & 1);
464*b285192aSMauro Carvalho Chehab 		reg_2920 = master_height >> 2;
465*b285192aSMauro Carvalho Chehab 		reg_2928 = master_height >> 2;
466*b285192aSMauro Carvalho Chehab 		reg_2930 = master_height;
467*b285192aSMauro Carvalho Chehab 		reg_2940 = master_height >> 1;
468*b285192aSMauro Carvalho Chehab 		reg_296c = 0x00000000;
469*b285192aSMauro Carvalho Chehab 		if (f->interlaced_y) {
470*b285192aSMauro Carvalho Chehab 			reg_2964_base >>= 3;
471*b285192aSMauro Carvalho Chehab 		} else {
472*b285192aSMauro Carvalho Chehab 			reg_296c++;
473*b285192aSMauro Carvalho Chehab 			reg_2964_base >>= 2;
474*b285192aSMauro Carvalho Chehab 		}
475*b285192aSMauro Carvalho Chehab 		if (f->interlaced_uv)
476*b285192aSMauro Carvalho Chehab 			reg_2928 >>= 1;
477*b285192aSMauro Carvalho Chehab 		reg_2968_base >>= 3;
478*b285192aSMauro Carvalho Chehab 	} else if (f->dst_h >= f->src_h / 2) {
479*b285192aSMauro Carvalho Chehab 		master_height = (f->src_h * 0x00200000) / f->dst_h;
480*b285192aSMauro Carvalho Chehab 		master_height = (master_height >> 1) + (master_height & 1);
481*b285192aSMauro Carvalho Chehab 		reg_2920 = master_height >> 2;
482*b285192aSMauro Carvalho Chehab 		reg_2928 = master_height >> 2;
483*b285192aSMauro Carvalho Chehab 		reg_2930 = master_height;
484*b285192aSMauro Carvalho Chehab 		reg_2940 = master_height;
485*b285192aSMauro Carvalho Chehab 		reg_296c = 0x00000101;
486*b285192aSMauro Carvalho Chehab 		if (f->interlaced_y) {
487*b285192aSMauro Carvalho Chehab 			reg_2964_base >>= 2;
488*b285192aSMauro Carvalho Chehab 		} else {
489*b285192aSMauro Carvalho Chehab 			reg_296c++;
490*b285192aSMauro Carvalho Chehab 			reg_2964_base >>= 1;
491*b285192aSMauro Carvalho Chehab 		}
492*b285192aSMauro Carvalho Chehab 		if (f->interlaced_uv)
493*b285192aSMauro Carvalho Chehab 			reg_2928 >>= 1;
494*b285192aSMauro Carvalho Chehab 		reg_2968_base >>= 2;
495*b285192aSMauro Carvalho Chehab 	} else {
496*b285192aSMauro Carvalho Chehab 		master_height = (f->src_h * 0x00100000) / f->dst_h;
497*b285192aSMauro Carvalho Chehab 		master_height = (master_height >> 1) + (master_height & 1);
498*b285192aSMauro Carvalho Chehab 		reg_2920 = master_height >> 2;
499*b285192aSMauro Carvalho Chehab 		reg_2928 = master_height >> 2;
500*b285192aSMauro Carvalho Chehab 		reg_2930 = master_height;
501*b285192aSMauro Carvalho Chehab 		reg_2940 = master_height;
502*b285192aSMauro Carvalho Chehab 		reg_2964_base >>= 1;
503*b285192aSMauro Carvalho Chehab 		reg_2968_base >>= 2;
504*b285192aSMauro Carvalho Chehab 		reg_296c = 0x00000102;
505*b285192aSMauro Carvalho Chehab 	}
506*b285192aSMauro Carvalho Chehab 
507*b285192aSMauro Carvalho Chehab 	/* FIXME These registers change depending on scaled / unscaled output
508*b285192aSMauro Carvalho Chehab 	   We really need to work out what they should be */
509*b285192aSMauro Carvalho Chehab 	if (f->src_h == f->dst_h) {
510*b285192aSMauro Carvalho Chehab 		reg_2934 = 0x00020000;
511*b285192aSMauro Carvalho Chehab 		reg_293c = 0x00100000;
512*b285192aSMauro Carvalho Chehab 		reg_2944 = 0x00040000;
513*b285192aSMauro Carvalho Chehab 		reg_294c = 0x000b0000;
514*b285192aSMauro Carvalho Chehab 	} else {
515*b285192aSMauro Carvalho Chehab 		reg_2934 = 0x00000FF0;
516*b285192aSMauro Carvalho Chehab 		reg_293c = 0x00000FF0;
517*b285192aSMauro Carvalho Chehab 		reg_2944 = 0x00000FF0;
518*b285192aSMauro Carvalho Chehab 		reg_294c = 0x00000FF0;
519*b285192aSMauro Carvalho Chehab 	}
520*b285192aSMauro Carvalho Chehab 
521*b285192aSMauro Carvalho Chehab 	/* The first line to be displayed */
522*b285192aSMauro Carvalho Chehab 	reg_2950 = 0x00010000 + src_major_y;
523*b285192aSMauro Carvalho Chehab 	if (f->interlaced_y)
524*b285192aSMauro Carvalho Chehab 		reg_2950 += 0x00010000;
525*b285192aSMauro Carvalho Chehab 	reg_2954 = reg_2950 + 1;
526*b285192aSMauro Carvalho Chehab 
527*b285192aSMauro Carvalho Chehab 	reg_2958 = 0x00010000 + (src_major_y >> 1);
528*b285192aSMauro Carvalho Chehab 	if (f->interlaced_uv)
529*b285192aSMauro Carvalho Chehab 		reg_2958 += 0x00010000;
530*b285192aSMauro Carvalho Chehab 	reg_295c = reg_2958 + 1;
531*b285192aSMauro Carvalho Chehab 
532*b285192aSMauro Carvalho Chehab 	if (yi->decode_height == 480)
533*b285192aSMauro Carvalho Chehab 		reg_289c = 0x011e0017;
534*b285192aSMauro Carvalho Chehab 	else
535*b285192aSMauro Carvalho Chehab 		reg_289c = 0x01500017;
536*b285192aSMauro Carvalho Chehab 
537*b285192aSMauro Carvalho Chehab 	if (f->dst_y < 0)
538*b285192aSMauro Carvalho Chehab 		reg_289c = (reg_289c - ((f->dst_y & ~1)<<15))-(f->dst_y >>1);
539*b285192aSMauro Carvalho Chehab 	else
540*b285192aSMauro Carvalho Chehab 		reg_289c = (reg_289c + ((f->dst_y & ~1)<<15))+(f->dst_y >>1);
541*b285192aSMauro Carvalho Chehab 
542*b285192aSMauro Carvalho Chehab 	/* How much of the source to decode.
543*b285192aSMauro Carvalho Chehab 	   Take into account the source offset */
544*b285192aSMauro Carvalho Chehab 	reg_2960 = ((src_minor_y + f->src_h + src_major_y) - 1) |
545*b285192aSMauro Carvalho Chehab 		(((src_minor_uv + f->src_h + src_major_uv - 1) & ~1) << 15);
546*b285192aSMauro Carvalho Chehab 
547*b285192aSMauro Carvalho Chehab 	/* Calculate correct value for register 2964 */
548*b285192aSMauro Carvalho Chehab 	if (f->src_h == f->dst_h) {
549*b285192aSMauro Carvalho Chehab 		reg_2964 = 1;
550*b285192aSMauro Carvalho Chehab 	} else {
551*b285192aSMauro Carvalho Chehab 		reg_2964 = 2 + ((f->dst_h << 1) / f->src_h);
552*b285192aSMauro Carvalho Chehab 		reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1);
553*b285192aSMauro Carvalho Chehab 	}
554*b285192aSMauro Carvalho Chehab 	reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1);
555*b285192aSMauro Carvalho Chehab 	reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94);
556*b285192aSMauro Carvalho Chehab 
557*b285192aSMauro Carvalho Chehab 	/* Okay, we've wasted time working out the correct value,
558*b285192aSMauro Carvalho Chehab 	   but if we use it, it fouls the the window alignment.
559*b285192aSMauro Carvalho Chehab 	   Fudge it to what we want... */
560*b285192aSMauro Carvalho Chehab 	reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16));
561*b285192aSMauro Carvalho Chehab 	reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16));
562*b285192aSMauro Carvalho Chehab 
563*b285192aSMauro Carvalho Chehab 	/* Deviate further from what it should be. I find the flicker headache
564*b285192aSMauro Carvalho Chehab 	   inducing so try to reduce it slightly. Leave 2968 as-is otherwise
565*b285192aSMauro Carvalho Chehab 	   colours foul. */
566*b285192aSMauro Carvalho Chehab 	if ((reg_2964 != 0x00010001) && (f->dst_h / 2 <= f->src_h))
567*b285192aSMauro Carvalho Chehab 		reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF) / 2);
568*b285192aSMauro Carvalho Chehab 
569*b285192aSMauro Carvalho Chehab 	if (!f->interlaced_y)
570*b285192aSMauro Carvalho Chehab 		reg_2964 -= 0x00010001;
571*b285192aSMauro Carvalho Chehab 	if (!f->interlaced_uv)
572*b285192aSMauro Carvalho Chehab 		reg_2968 -= 0x00010001;
573*b285192aSMauro Carvalho Chehab 
574*b285192aSMauro Carvalho Chehab 	reg_2964 += ((reg_2964_base << 16) | reg_2964_base);
575*b285192aSMauro Carvalho Chehab 	reg_2968 += ((reg_2968_base << 16) | reg_2968_base);
576*b285192aSMauro Carvalho Chehab 
577*b285192aSMauro Carvalho Chehab 	/* Select the vertical filter */
578*b285192aSMauro Carvalho Chehab 	if (f->src_h == f->dst_h) {
579*b285192aSMauro Carvalho Chehab 		/* An exact size match uses filter 0/1 */
580*b285192aSMauro Carvalho Chehab 		v_filter_1 = 0;
581*b285192aSMauro Carvalho Chehab 		v_filter_2 = 1;
582*b285192aSMauro Carvalho Chehab 	} else {
583*b285192aSMauro Carvalho Chehab 		/* Figure out which filter to use */
584*b285192aSMauro Carvalho Chehab 		v_filter_1 = ((f->src_h << 16) / f->dst_h) >> 15;
585*b285192aSMauro Carvalho Chehab 		v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
586*b285192aSMauro Carvalho Chehab 		/* Only an exact size match can use filter 0 */
587*b285192aSMauro Carvalho Chehab 		v_filter_1 += !v_filter_1;
588*b285192aSMauro Carvalho Chehab 		v_filter_2 = v_filter_1;
589*b285192aSMauro Carvalho Chehab 	}
590*b285192aSMauro Carvalho Chehab 
591*b285192aSMauro Carvalho Chehab 	write_reg(reg_2934, 0x02934);
592*b285192aSMauro Carvalho Chehab 	write_reg(reg_293c, 0x0293c);
593*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",
594*b285192aSMauro Carvalho Chehab 		       yi->reg_2934, reg_2934, yi->reg_293c, reg_293c);
595*b285192aSMauro Carvalho Chehab 	write_reg(reg_2944, 0x02944);
596*b285192aSMauro Carvalho Chehab 	write_reg(reg_294c, 0x0294c);
597*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",
598*b285192aSMauro Carvalho Chehab 		       yi->reg_2944, reg_2944, yi->reg_294c, reg_294c);
599*b285192aSMauro Carvalho Chehab 
600*b285192aSMauro Carvalho Chehab 	/* Ensure 2970 is 0 (does it ever change ?) */
601*b285192aSMauro Carvalho Chehab /*	write_reg(0,0x02970); */
602*b285192aSMauro Carvalho Chehab /*	IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n", yi->reg_2970, 0); */
603*b285192aSMauro Carvalho Chehab 
604*b285192aSMauro Carvalho Chehab 	write_reg(reg_2930, 0x02938);
605*b285192aSMauro Carvalho Chehab 	write_reg(reg_2930, 0x02930);
606*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",
607*b285192aSMauro Carvalho Chehab 		       yi->reg_2930, reg_2930, yi->reg_2938, reg_2930);
608*b285192aSMauro Carvalho Chehab 
609*b285192aSMauro Carvalho Chehab 	write_reg(reg_2928, 0x02928);
610*b285192aSMauro Carvalho Chehab 	write_reg(reg_2928 + 0x514, 0x0292C);
611*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",
612*b285192aSMauro Carvalho Chehab 		       yi->reg_2928, reg_2928, yi->reg_292c, reg_2928 + 0x514);
613*b285192aSMauro Carvalho Chehab 
614*b285192aSMauro Carvalho Chehab 	write_reg(reg_2920, 0x02920);
615*b285192aSMauro Carvalho Chehab 	write_reg(reg_2920 + 0x514, 0x02924);
616*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",
617*b285192aSMauro Carvalho Chehab 		       yi->reg_2920, reg_2920, yi->reg_2924, reg_2920 + 0x514);
618*b285192aSMauro Carvalho Chehab 
619*b285192aSMauro Carvalho Chehab 	write_reg(reg_2918, 0x02918);
620*b285192aSMauro Carvalho Chehab 	write_reg(reg_291c, 0x0291C);
621*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",
622*b285192aSMauro Carvalho Chehab 		       yi->reg_2918, reg_2918, yi->reg_291c, reg_291c);
623*b285192aSMauro Carvalho Chehab 
624*b285192aSMauro Carvalho Chehab 	write_reg(reg_296c, 0x0296c);
625*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",
626*b285192aSMauro Carvalho Chehab 		       yi->reg_296c, reg_296c);
627*b285192aSMauro Carvalho Chehab 
628*b285192aSMauro Carvalho Chehab 	write_reg(reg_2940, 0x02948);
629*b285192aSMauro Carvalho Chehab 	write_reg(reg_2940, 0x02940);
630*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",
631*b285192aSMauro Carvalho Chehab 		       yi->reg_2940, reg_2940, yi->reg_2948, reg_2940);
632*b285192aSMauro Carvalho Chehab 
633*b285192aSMauro Carvalho Chehab 	write_reg(reg_2950, 0x02950);
634*b285192aSMauro Carvalho Chehab 	write_reg(reg_2954, 0x02954);
635*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",
636*b285192aSMauro Carvalho Chehab 		       yi->reg_2950, reg_2950, yi->reg_2954, reg_2954);
637*b285192aSMauro Carvalho Chehab 
638*b285192aSMauro Carvalho Chehab 	write_reg(reg_2958, 0x02958);
639*b285192aSMauro Carvalho Chehab 	write_reg(reg_295c, 0x0295C);
640*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",
641*b285192aSMauro Carvalho Chehab 		       yi->reg_2958, reg_2958, yi->reg_295c, reg_295c);
642*b285192aSMauro Carvalho Chehab 
643*b285192aSMauro Carvalho Chehab 	write_reg(reg_2960, 0x02960);
644*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",
645*b285192aSMauro Carvalho Chehab 		       yi->reg_2960, reg_2960);
646*b285192aSMauro Carvalho Chehab 
647*b285192aSMauro Carvalho Chehab 	write_reg(reg_2964, 0x02964);
648*b285192aSMauro Carvalho Chehab 	write_reg(reg_2968, 0x02968);
649*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",
650*b285192aSMauro Carvalho Chehab 		       yi->reg_2964, reg_2964, yi->reg_2968, reg_2968);
651*b285192aSMauro Carvalho Chehab 
652*b285192aSMauro Carvalho Chehab 	write_reg(reg_289c, 0x0289c);
653*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",
654*b285192aSMauro Carvalho Chehab 		       yi->reg_289c, reg_289c);
655*b285192aSMauro Carvalho Chehab 
656*b285192aSMauro Carvalho Chehab 	/* Only update filter 1 if we really need to */
657*b285192aSMauro Carvalho Chehab 	if (v_filter_1 != yi->v_filter_1) {
658*b285192aSMauro Carvalho Chehab 		ivtv_yuv_filter(itv, -1, v_filter_1, -1);
659*b285192aSMauro Carvalho Chehab 		yi->v_filter_1 = v_filter_1;
660*b285192aSMauro Carvalho Chehab 	}
661*b285192aSMauro Carvalho Chehab 
662*b285192aSMauro Carvalho Chehab 	/* Only update filter 2 if we really need to */
663*b285192aSMauro Carvalho Chehab 	if (v_filter_2 != yi->v_filter_2) {
664*b285192aSMauro Carvalho Chehab 		ivtv_yuv_filter(itv, -1, -1, v_filter_2);
665*b285192aSMauro Carvalho Chehab 		yi->v_filter_2 = v_filter_2;
666*b285192aSMauro Carvalho Chehab 	}
667*b285192aSMauro Carvalho Chehab }
668*b285192aSMauro Carvalho Chehab 
669*b285192aSMauro Carvalho Chehab /* Modify the supplied coordinate information to fit the visible osd area */
670*b285192aSMauro Carvalho Chehab static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f)
671*b285192aSMauro Carvalho Chehab {
672*b285192aSMauro Carvalho Chehab 	struct yuv_frame_info *of = &itv->yuv_info.old_frame_info;
673*b285192aSMauro Carvalho Chehab 	int osd_crop;
674*b285192aSMauro Carvalho Chehab 	u32 osd_scale;
675*b285192aSMauro Carvalho Chehab 	u32 yuv_update = 0;
676*b285192aSMauro Carvalho Chehab 
677*b285192aSMauro Carvalho Chehab 	/* Sorry, but no negative coords for src */
678*b285192aSMauro Carvalho Chehab 	if (f->src_x < 0)
679*b285192aSMauro Carvalho Chehab 		f->src_x = 0;
680*b285192aSMauro Carvalho Chehab 	if (f->src_y < 0)
681*b285192aSMauro Carvalho Chehab 		f->src_y = 0;
682*b285192aSMauro Carvalho Chehab 
683*b285192aSMauro Carvalho Chehab 	/* Can only reduce width down to 1/4 original size */
684*b285192aSMauro Carvalho Chehab 	if ((osd_crop = f->src_w - 4 * f->dst_w) > 0) {
685*b285192aSMauro Carvalho Chehab 		f->src_x += osd_crop / 2;
686*b285192aSMauro Carvalho Chehab 		f->src_w = (f->src_w - osd_crop) & ~3;
687*b285192aSMauro Carvalho Chehab 		f->dst_w = f->src_w / 4;
688*b285192aSMauro Carvalho Chehab 		f->dst_w += f->dst_w & 1;
689*b285192aSMauro Carvalho Chehab 	}
690*b285192aSMauro Carvalho Chehab 
691*b285192aSMauro Carvalho Chehab 	/* Can only reduce height down to 1/4 original size */
692*b285192aSMauro Carvalho Chehab 	if (f->src_h / f->dst_h >= 2) {
693*b285192aSMauro Carvalho Chehab 		/* Overflow may be because we're running progressive,
694*b285192aSMauro Carvalho Chehab 		   so force mode switch */
695*b285192aSMauro Carvalho Chehab 		f->interlaced_y = 1;
696*b285192aSMauro Carvalho Chehab 		/* Make sure we're still within limits for interlace */
697*b285192aSMauro Carvalho Chehab 		if ((osd_crop = f->src_h - 4 * f->dst_h) > 0) {
698*b285192aSMauro Carvalho Chehab 			/* If we reach here we'll have to force the height. */
699*b285192aSMauro Carvalho Chehab 			f->src_y += osd_crop / 2;
700*b285192aSMauro Carvalho Chehab 			f->src_h = (f->src_h - osd_crop) & ~3;
701*b285192aSMauro Carvalho Chehab 			f->dst_h = f->src_h / 4;
702*b285192aSMauro Carvalho Chehab 			f->dst_h += f->dst_h & 1;
703*b285192aSMauro Carvalho Chehab 		}
704*b285192aSMauro Carvalho Chehab 	}
705*b285192aSMauro Carvalho Chehab 
706*b285192aSMauro Carvalho Chehab 	/* If there's nothing to safe to display, we may as well stop now */
707*b285192aSMauro Carvalho Chehab 	if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 ||
708*b285192aSMauro Carvalho Chehab 	    (int)f->src_w <= 2 || (int)f->src_h <= 2) {
709*b285192aSMauro Carvalho Chehab 		return IVTV_YUV_UPDATE_INVALID;
710*b285192aSMauro Carvalho Chehab 	}
711*b285192aSMauro Carvalho Chehab 
712*b285192aSMauro Carvalho Chehab 	/* Ensure video remains inside OSD area */
713*b285192aSMauro Carvalho Chehab 	osd_scale = (f->src_h << 16) / f->dst_h;
714*b285192aSMauro Carvalho Chehab 
715*b285192aSMauro Carvalho Chehab 	if ((osd_crop = f->pan_y - f->dst_y) > 0) {
716*b285192aSMauro Carvalho Chehab 		/* Falls off the upper edge - crop */
717*b285192aSMauro Carvalho Chehab 		f->src_y += (osd_scale * osd_crop) >> 16;
718*b285192aSMauro Carvalho Chehab 		f->src_h -= (osd_scale * osd_crop) >> 16;
719*b285192aSMauro Carvalho Chehab 		f->dst_h -= osd_crop;
720*b285192aSMauro Carvalho Chehab 		f->dst_y = 0;
721*b285192aSMauro Carvalho Chehab 	} else {
722*b285192aSMauro Carvalho Chehab 		f->dst_y -= f->pan_y;
723*b285192aSMauro Carvalho Chehab 	}
724*b285192aSMauro Carvalho Chehab 
725*b285192aSMauro Carvalho Chehab 	if ((osd_crop = f->dst_h + f->dst_y - f->vis_h) > 0) {
726*b285192aSMauro Carvalho Chehab 		/* Falls off the lower edge - crop */
727*b285192aSMauro Carvalho Chehab 		f->dst_h -= osd_crop;
728*b285192aSMauro Carvalho Chehab 		f->src_h -= (osd_scale * osd_crop) >> 16;
729*b285192aSMauro Carvalho Chehab 	}
730*b285192aSMauro Carvalho Chehab 
731*b285192aSMauro Carvalho Chehab 	osd_scale = (f->src_w << 16) / f->dst_w;
732*b285192aSMauro Carvalho Chehab 
733*b285192aSMauro Carvalho Chehab 	if ((osd_crop = f->pan_x - f->dst_x) > 0) {
734*b285192aSMauro Carvalho Chehab 		/* Fall off the left edge - crop */
735*b285192aSMauro Carvalho Chehab 		f->src_x += (osd_scale * osd_crop) >> 16;
736*b285192aSMauro Carvalho Chehab 		f->src_w -= (osd_scale * osd_crop) >> 16;
737*b285192aSMauro Carvalho Chehab 		f->dst_w -= osd_crop;
738*b285192aSMauro Carvalho Chehab 		f->dst_x = 0;
739*b285192aSMauro Carvalho Chehab 	} else {
740*b285192aSMauro Carvalho Chehab 		f->dst_x -= f->pan_x;
741*b285192aSMauro Carvalho Chehab 	}
742*b285192aSMauro Carvalho Chehab 
743*b285192aSMauro Carvalho Chehab 	if ((osd_crop = f->dst_w + f->dst_x - f->vis_w) > 0) {
744*b285192aSMauro Carvalho Chehab 		/* Falls off the right edge - crop */
745*b285192aSMauro Carvalho Chehab 		f->dst_w -= osd_crop;
746*b285192aSMauro Carvalho Chehab 		f->src_w -= (osd_scale * osd_crop) >> 16;
747*b285192aSMauro Carvalho Chehab 	}
748*b285192aSMauro Carvalho Chehab 
749*b285192aSMauro Carvalho Chehab 	if (itv->yuv_info.track_osd) {
750*b285192aSMauro Carvalho Chehab 		/* The OSD can be moved. Track to it */
751*b285192aSMauro Carvalho Chehab 		f->dst_x += itv->yuv_info.osd_x_offset;
752*b285192aSMauro Carvalho Chehab 		f->dst_y += itv->yuv_info.osd_y_offset;
753*b285192aSMauro Carvalho Chehab 	}
754*b285192aSMauro Carvalho Chehab 
755*b285192aSMauro Carvalho Chehab 	/* Width & height for both src & dst must be even.
756*b285192aSMauro Carvalho Chehab 	   Same for coordinates. */
757*b285192aSMauro Carvalho Chehab 	f->dst_w &= ~1;
758*b285192aSMauro Carvalho Chehab 	f->dst_x &= ~1;
759*b285192aSMauro Carvalho Chehab 
760*b285192aSMauro Carvalho Chehab 	f->src_w += f->src_x & 1;
761*b285192aSMauro Carvalho Chehab 	f->src_x &= ~1;
762*b285192aSMauro Carvalho Chehab 
763*b285192aSMauro Carvalho Chehab 	f->src_w &= ~1;
764*b285192aSMauro Carvalho Chehab 	f->dst_w &= ~1;
765*b285192aSMauro Carvalho Chehab 
766*b285192aSMauro Carvalho Chehab 	f->dst_h &= ~1;
767*b285192aSMauro Carvalho Chehab 	f->dst_y &= ~1;
768*b285192aSMauro Carvalho Chehab 
769*b285192aSMauro Carvalho Chehab 	f->src_h += f->src_y & 1;
770*b285192aSMauro Carvalho Chehab 	f->src_y &= ~1;
771*b285192aSMauro Carvalho Chehab 
772*b285192aSMauro Carvalho Chehab 	f->src_h &= ~1;
773*b285192aSMauro Carvalho Chehab 	f->dst_h &= ~1;
774*b285192aSMauro Carvalho Chehab 
775*b285192aSMauro Carvalho Chehab 	/* Due to rounding, we may have reduced the output size to <1/4 of
776*b285192aSMauro Carvalho Chehab 	   the source. Check again, but this time just resize. Don't change
777*b285192aSMauro Carvalho Chehab 	   source coordinates */
778*b285192aSMauro Carvalho Chehab 	if (f->dst_w < f->src_w / 4) {
779*b285192aSMauro Carvalho Chehab 		f->src_w &= ~3;
780*b285192aSMauro Carvalho Chehab 		f->dst_w = f->src_w / 4;
781*b285192aSMauro Carvalho Chehab 		f->dst_w += f->dst_w & 1;
782*b285192aSMauro Carvalho Chehab 	}
783*b285192aSMauro Carvalho Chehab 	if (f->dst_h < f->src_h / 4) {
784*b285192aSMauro Carvalho Chehab 		f->src_h &= ~3;
785*b285192aSMauro Carvalho Chehab 		f->dst_h = f->src_h / 4;
786*b285192aSMauro Carvalho Chehab 		f->dst_h += f->dst_h & 1;
787*b285192aSMauro Carvalho Chehab 	}
788*b285192aSMauro Carvalho Chehab 
789*b285192aSMauro Carvalho Chehab 	/* Check again. If there's nothing to safe to display, stop now */
790*b285192aSMauro Carvalho Chehab 	if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 ||
791*b285192aSMauro Carvalho Chehab 	    (int)f->src_w <= 2 || (int)f->src_h <= 2) {
792*b285192aSMauro Carvalho Chehab 		return IVTV_YUV_UPDATE_INVALID;
793*b285192aSMauro Carvalho Chehab 	}
794*b285192aSMauro Carvalho Chehab 
795*b285192aSMauro Carvalho Chehab 	/* Both x offset & width are linked, so they have to be done together */
796*b285192aSMauro Carvalho Chehab 	if ((of->dst_w != f->dst_w) || (of->src_w != f->src_w) ||
797*b285192aSMauro Carvalho Chehab 	    (of->dst_x != f->dst_x) || (of->src_x != f->src_x) ||
798*b285192aSMauro Carvalho Chehab 	    (of->pan_x != f->pan_x) || (of->vis_w != f->vis_w)) {
799*b285192aSMauro Carvalho Chehab 		yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL;
800*b285192aSMauro Carvalho Chehab 	}
801*b285192aSMauro Carvalho Chehab 
802*b285192aSMauro Carvalho Chehab 	if ((of->src_h != f->src_h) || (of->dst_h != f->dst_h) ||
803*b285192aSMauro Carvalho Chehab 	    (of->dst_y != f->dst_y) || (of->src_y != f->src_y) ||
804*b285192aSMauro Carvalho Chehab 	    (of->pan_y != f->pan_y) || (of->vis_h != f->vis_h) ||
805*b285192aSMauro Carvalho Chehab 	    (of->lace_mode != f->lace_mode) ||
806*b285192aSMauro Carvalho Chehab 	    (of->interlaced_y != f->interlaced_y) ||
807*b285192aSMauro Carvalho Chehab 	    (of->interlaced_uv != f->interlaced_uv)) {
808*b285192aSMauro Carvalho Chehab 		yuv_update |= IVTV_YUV_UPDATE_VERTICAL;
809*b285192aSMauro Carvalho Chehab 	}
810*b285192aSMauro Carvalho Chehab 
811*b285192aSMauro Carvalho Chehab 	return yuv_update;
812*b285192aSMauro Carvalho Chehab }
813*b285192aSMauro Carvalho Chehab 
814*b285192aSMauro Carvalho Chehab /* Update the scaling register to the requested value */
815*b285192aSMauro Carvalho Chehab void ivtv_yuv_work_handler(struct ivtv *itv)
816*b285192aSMauro Carvalho Chehab {
817*b285192aSMauro Carvalho Chehab 	struct yuv_playback_info *yi = &itv->yuv_info;
818*b285192aSMauro Carvalho Chehab 	struct yuv_frame_info f;
819*b285192aSMauro Carvalho Chehab 	int frame = yi->update_frame;
820*b285192aSMauro Carvalho Chehab 	u32 yuv_update;
821*b285192aSMauro Carvalho Chehab 
822*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("Update yuv registers for frame %d\n", frame);
823*b285192aSMauro Carvalho Chehab 	f = yi->new_frame_info[frame];
824*b285192aSMauro Carvalho Chehab 
825*b285192aSMauro Carvalho Chehab 	if (yi->track_osd) {
826*b285192aSMauro Carvalho Chehab 		/* Snapshot the osd pan info */
827*b285192aSMauro Carvalho Chehab 		f.pan_x = yi->osd_x_pan;
828*b285192aSMauro Carvalho Chehab 		f.pan_y = yi->osd_y_pan;
829*b285192aSMauro Carvalho Chehab 		f.vis_w = yi->osd_vis_w;
830*b285192aSMauro Carvalho Chehab 		f.vis_h = yi->osd_vis_h;
831*b285192aSMauro Carvalho Chehab 	} else {
832*b285192aSMauro Carvalho Chehab 		/* Not tracking the osd, so assume full screen */
833*b285192aSMauro Carvalho Chehab 		f.pan_x = 0;
834*b285192aSMauro Carvalho Chehab 		f.pan_y = 0;
835*b285192aSMauro Carvalho Chehab 		f.vis_w = 720;
836*b285192aSMauro Carvalho Chehab 		f.vis_h = yi->decode_height;
837*b285192aSMauro Carvalho Chehab 	}
838*b285192aSMauro Carvalho Chehab 
839*b285192aSMauro Carvalho Chehab 	/* Calculate the display window coordinates. Exit if nothing left */
840*b285192aSMauro Carvalho Chehab 	if (!(yuv_update = ivtv_yuv_window_setup(itv, &f)))
841*b285192aSMauro Carvalho Chehab 		return;
842*b285192aSMauro Carvalho Chehab 
843*b285192aSMauro Carvalho Chehab 	if (yuv_update & IVTV_YUV_UPDATE_INVALID) {
844*b285192aSMauro Carvalho Chehab 		write_reg(0x01008080, 0x2898);
845*b285192aSMauro Carvalho Chehab 	} else if (yuv_update) {
846*b285192aSMauro Carvalho Chehab 		write_reg(0x00108080, 0x2898);
847*b285192aSMauro Carvalho Chehab 
848*b285192aSMauro Carvalho Chehab 		if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL)
849*b285192aSMauro Carvalho Chehab 			ivtv_yuv_handle_horizontal(itv, &f);
850*b285192aSMauro Carvalho Chehab 
851*b285192aSMauro Carvalho Chehab 		if (yuv_update & IVTV_YUV_UPDATE_VERTICAL)
852*b285192aSMauro Carvalho Chehab 			ivtv_yuv_handle_vertical(itv, &f);
853*b285192aSMauro Carvalho Chehab 	}
854*b285192aSMauro Carvalho Chehab 	yi->old_frame_info = f;
855*b285192aSMauro Carvalho Chehab }
856*b285192aSMauro Carvalho Chehab 
857*b285192aSMauro Carvalho Chehab static void ivtv_yuv_init(struct ivtv *itv)
858*b285192aSMauro Carvalho Chehab {
859*b285192aSMauro Carvalho Chehab 	struct yuv_playback_info *yi = &itv->yuv_info;
860*b285192aSMauro Carvalho Chehab 
861*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("ivtv_yuv_init\n");
862*b285192aSMauro Carvalho Chehab 
863*b285192aSMauro Carvalho Chehab 	/* Take a snapshot of the current register settings */
864*b285192aSMauro Carvalho Chehab 	yi->reg_2834 = read_reg(0x02834);
865*b285192aSMauro Carvalho Chehab 	yi->reg_2838 = read_reg(0x02838);
866*b285192aSMauro Carvalho Chehab 	yi->reg_283c = read_reg(0x0283c);
867*b285192aSMauro Carvalho Chehab 	yi->reg_2840 = read_reg(0x02840);
868*b285192aSMauro Carvalho Chehab 	yi->reg_2844 = read_reg(0x02844);
869*b285192aSMauro Carvalho Chehab 	yi->reg_2848 = read_reg(0x02848);
870*b285192aSMauro Carvalho Chehab 	yi->reg_2854 = read_reg(0x02854);
871*b285192aSMauro Carvalho Chehab 	yi->reg_285c = read_reg(0x0285c);
872*b285192aSMauro Carvalho Chehab 	yi->reg_2864 = read_reg(0x02864);
873*b285192aSMauro Carvalho Chehab 	yi->reg_2870 = read_reg(0x02870);
874*b285192aSMauro Carvalho Chehab 	yi->reg_2874 = read_reg(0x02874);
875*b285192aSMauro Carvalho Chehab 	yi->reg_2898 = read_reg(0x02898);
876*b285192aSMauro Carvalho Chehab 	yi->reg_2890 = read_reg(0x02890);
877*b285192aSMauro Carvalho Chehab 
878*b285192aSMauro Carvalho Chehab 	yi->reg_289c = read_reg(0x0289c);
879*b285192aSMauro Carvalho Chehab 	yi->reg_2918 = read_reg(0x02918);
880*b285192aSMauro Carvalho Chehab 	yi->reg_291c = read_reg(0x0291c);
881*b285192aSMauro Carvalho Chehab 	yi->reg_2920 = read_reg(0x02920);
882*b285192aSMauro Carvalho Chehab 	yi->reg_2924 = read_reg(0x02924);
883*b285192aSMauro Carvalho Chehab 	yi->reg_2928 = read_reg(0x02928);
884*b285192aSMauro Carvalho Chehab 	yi->reg_292c = read_reg(0x0292c);
885*b285192aSMauro Carvalho Chehab 	yi->reg_2930 = read_reg(0x02930);
886*b285192aSMauro Carvalho Chehab 	yi->reg_2934 = read_reg(0x02934);
887*b285192aSMauro Carvalho Chehab 	yi->reg_2938 = read_reg(0x02938);
888*b285192aSMauro Carvalho Chehab 	yi->reg_293c = read_reg(0x0293c);
889*b285192aSMauro Carvalho Chehab 	yi->reg_2940 = read_reg(0x02940);
890*b285192aSMauro Carvalho Chehab 	yi->reg_2944 = read_reg(0x02944);
891*b285192aSMauro Carvalho Chehab 	yi->reg_2948 = read_reg(0x02948);
892*b285192aSMauro Carvalho Chehab 	yi->reg_294c = read_reg(0x0294c);
893*b285192aSMauro Carvalho Chehab 	yi->reg_2950 = read_reg(0x02950);
894*b285192aSMauro Carvalho Chehab 	yi->reg_2954 = read_reg(0x02954);
895*b285192aSMauro Carvalho Chehab 	yi->reg_2958 = read_reg(0x02958);
896*b285192aSMauro Carvalho Chehab 	yi->reg_295c = read_reg(0x0295c);
897*b285192aSMauro Carvalho Chehab 	yi->reg_2960 = read_reg(0x02960);
898*b285192aSMauro Carvalho Chehab 	yi->reg_2964 = read_reg(0x02964);
899*b285192aSMauro Carvalho Chehab 	yi->reg_2968 = read_reg(0x02968);
900*b285192aSMauro Carvalho Chehab 	yi->reg_296c = read_reg(0x0296c);
901*b285192aSMauro Carvalho Chehab 	yi->reg_2970 = read_reg(0x02970);
902*b285192aSMauro Carvalho Chehab 
903*b285192aSMauro Carvalho Chehab 	yi->v_filter_1 = -1;
904*b285192aSMauro Carvalho Chehab 	yi->v_filter_2 = -1;
905*b285192aSMauro Carvalho Chehab 	yi->h_filter = -1;
906*b285192aSMauro Carvalho Chehab 
907*b285192aSMauro Carvalho Chehab 	/* Set some valid size info */
908*b285192aSMauro Carvalho Chehab 	yi->osd_x_offset = read_reg(0x02a04) & 0x00000FFF;
909*b285192aSMauro Carvalho Chehab 	yi->osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF;
910*b285192aSMauro Carvalho Chehab 
911*b285192aSMauro Carvalho Chehab 	/* Bit 2 of reg 2878 indicates current decoder output format
912*b285192aSMauro Carvalho Chehab 	   0 : NTSC    1 : PAL */
913*b285192aSMauro Carvalho Chehab 	if (read_reg(0x2878) & 4)
914*b285192aSMauro Carvalho Chehab 		yi->decode_height = 576;
915*b285192aSMauro Carvalho Chehab 	else
916*b285192aSMauro Carvalho Chehab 		yi->decode_height = 480;
917*b285192aSMauro Carvalho Chehab 
918*b285192aSMauro Carvalho Chehab 	if (!itv->osd_info) {
919*b285192aSMauro Carvalho Chehab 		yi->osd_vis_w = 720 - yi->osd_x_offset;
920*b285192aSMauro Carvalho Chehab 		yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
921*b285192aSMauro Carvalho Chehab 	} else {
922*b285192aSMauro Carvalho Chehab 		/* If no visible size set, assume full size */
923*b285192aSMauro Carvalho Chehab 		if (!yi->osd_vis_w)
924*b285192aSMauro Carvalho Chehab 			yi->osd_vis_w = 720 - yi->osd_x_offset;
925*b285192aSMauro Carvalho Chehab 
926*b285192aSMauro Carvalho Chehab 		if (!yi->osd_vis_h) {
927*b285192aSMauro Carvalho Chehab 			yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
928*b285192aSMauro Carvalho Chehab 		} else if (yi->osd_vis_h + yi->osd_y_offset > yi->decode_height) {
929*b285192aSMauro Carvalho Chehab 			/* If output video standard has changed, requested height may
930*b285192aSMauro Carvalho Chehab 			   not be legal */
931*b285192aSMauro Carvalho Chehab 			IVTV_DEBUG_WARN("Clipping yuv output - fb size (%d) exceeds video standard limit (%d)\n",
932*b285192aSMauro Carvalho Chehab 					yi->osd_vis_h + yi->osd_y_offset,
933*b285192aSMauro Carvalho Chehab 					yi->decode_height);
934*b285192aSMauro Carvalho Chehab 			yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
935*b285192aSMauro Carvalho Chehab 		}
936*b285192aSMauro Carvalho Chehab 	}
937*b285192aSMauro Carvalho Chehab 
938*b285192aSMauro Carvalho Chehab 	/* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */
939*b285192aSMauro Carvalho Chehab 	yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL|__GFP_NOWARN);
940*b285192aSMauro Carvalho Chehab 	if (yi->blanking_ptr) {
941*b285192aSMauro Carvalho Chehab 		yi->blanking_dmaptr = pci_map_single(itv->pdev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE);
942*b285192aSMauro Carvalho Chehab 	} else {
943*b285192aSMauro Carvalho Chehab 		yi->blanking_dmaptr = 0;
944*b285192aSMauro Carvalho Chehab 		IVTV_DEBUG_WARN("Failed to allocate yuv blanking buffer\n");
945*b285192aSMauro Carvalho Chehab 	}
946*b285192aSMauro Carvalho Chehab 
947*b285192aSMauro Carvalho Chehab 	/* Enable YUV decoder output */
948*b285192aSMauro Carvalho Chehab 	write_reg_sync(0x01, IVTV_REG_VDM);
949*b285192aSMauro Carvalho Chehab 
950*b285192aSMauro Carvalho Chehab 	set_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
951*b285192aSMauro Carvalho Chehab 	atomic_set(&yi->next_dma_frame, 0);
952*b285192aSMauro Carvalho Chehab }
953*b285192aSMauro Carvalho Chehab 
954*b285192aSMauro Carvalho Chehab /* Get next available yuv buffer on PVR350 */
955*b285192aSMauro Carvalho Chehab static void ivtv_yuv_next_free(struct ivtv *itv)
956*b285192aSMauro Carvalho Chehab {
957*b285192aSMauro Carvalho Chehab 	int draw, display;
958*b285192aSMauro Carvalho Chehab 	struct yuv_playback_info *yi = &itv->yuv_info;
959*b285192aSMauro Carvalho Chehab 
960*b285192aSMauro Carvalho Chehab 	if (atomic_read(&yi->next_dma_frame) == -1)
961*b285192aSMauro Carvalho Chehab 		ivtv_yuv_init(itv);
962*b285192aSMauro Carvalho Chehab 
963*b285192aSMauro Carvalho Chehab 	draw = atomic_read(&yi->next_fill_frame);
964*b285192aSMauro Carvalho Chehab 	display = atomic_read(&yi->next_dma_frame);
965*b285192aSMauro Carvalho Chehab 
966*b285192aSMauro Carvalho Chehab 	if (display > draw)
967*b285192aSMauro Carvalho Chehab 		display -= IVTV_YUV_BUFFERS;
968*b285192aSMauro Carvalho Chehab 
969*b285192aSMauro Carvalho Chehab 	if (draw - display >= yi->max_frames_buffered)
970*b285192aSMauro Carvalho Chehab 		draw = (u8)(draw - 1) % IVTV_YUV_BUFFERS;
971*b285192aSMauro Carvalho Chehab 	else
972*b285192aSMauro Carvalho Chehab 		yi->new_frame_info[draw].update = 0;
973*b285192aSMauro Carvalho Chehab 
974*b285192aSMauro Carvalho Chehab 	yi->draw_frame = draw;
975*b285192aSMauro Carvalho Chehab }
976*b285192aSMauro Carvalho Chehab 
977*b285192aSMauro Carvalho Chehab /* Set up frame according to ivtv_dma_frame parameters */
978*b285192aSMauro Carvalho Chehab static void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
979*b285192aSMauro Carvalho Chehab {
980*b285192aSMauro Carvalho Chehab 	struct yuv_playback_info *yi = &itv->yuv_info;
981*b285192aSMauro Carvalho Chehab 	u8 frame = yi->draw_frame;
982*b285192aSMauro Carvalho Chehab 	u8 last_frame = (u8)(frame - 1) % IVTV_YUV_BUFFERS;
983*b285192aSMauro Carvalho Chehab 	struct yuv_frame_info *nf = &yi->new_frame_info[frame];
984*b285192aSMauro Carvalho Chehab 	struct yuv_frame_info *of = &yi->new_frame_info[last_frame];
985*b285192aSMauro Carvalho Chehab 	int lace_threshold = yi->lace_threshold;
986*b285192aSMauro Carvalho Chehab 
987*b285192aSMauro Carvalho Chehab 	/* Preserve old update flag in case we're overwriting a queued frame */
988*b285192aSMauro Carvalho Chehab 	int update = nf->update;
989*b285192aSMauro Carvalho Chehab 
990*b285192aSMauro Carvalho Chehab 	/* Take a snapshot of the yuv coordinate information */
991*b285192aSMauro Carvalho Chehab 	nf->src_x = args->src.left;
992*b285192aSMauro Carvalho Chehab 	nf->src_y = args->src.top;
993*b285192aSMauro Carvalho Chehab 	nf->src_w = args->src.width;
994*b285192aSMauro Carvalho Chehab 	nf->src_h = args->src.height;
995*b285192aSMauro Carvalho Chehab 	nf->dst_x = args->dst.left;
996*b285192aSMauro Carvalho Chehab 	nf->dst_y = args->dst.top;
997*b285192aSMauro Carvalho Chehab 	nf->dst_w = args->dst.width;
998*b285192aSMauro Carvalho Chehab 	nf->dst_h = args->dst.height;
999*b285192aSMauro Carvalho Chehab 	nf->tru_x = args->dst.left;
1000*b285192aSMauro Carvalho Chehab 	nf->tru_w = args->src_width;
1001*b285192aSMauro Carvalho Chehab 	nf->tru_h = args->src_height;
1002*b285192aSMauro Carvalho Chehab 
1003*b285192aSMauro Carvalho Chehab 	/* Are we going to offset the Y plane */
1004*b285192aSMauro Carvalho Chehab 	nf->offset_y = (nf->tru_h + nf->src_x < 512 - 16) ? 1 : 0;
1005*b285192aSMauro Carvalho Chehab 
1006*b285192aSMauro Carvalho Chehab 	nf->update = 0;
1007*b285192aSMauro Carvalho Chehab 	nf->interlaced_y = 0;
1008*b285192aSMauro Carvalho Chehab 	nf->interlaced_uv = 0;
1009*b285192aSMauro Carvalho Chehab 	nf->delay = 0;
1010*b285192aSMauro Carvalho Chehab 	nf->sync_field = 0;
1011*b285192aSMauro Carvalho Chehab 	nf->lace_mode = yi->lace_mode & IVTV_YUV_MODE_MASK;
1012*b285192aSMauro Carvalho Chehab 
1013*b285192aSMauro Carvalho Chehab 	if (lace_threshold < 0)
1014*b285192aSMauro Carvalho Chehab 		lace_threshold = yi->decode_height - 1;
1015*b285192aSMauro Carvalho Chehab 
1016*b285192aSMauro Carvalho Chehab 	/* Work out the lace settings */
1017*b285192aSMauro Carvalho Chehab 	switch (nf->lace_mode) {
1018*b285192aSMauro Carvalho Chehab 	case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */
1019*b285192aSMauro Carvalho Chehab 		nf->interlaced = 0;
1020*b285192aSMauro Carvalho Chehab 		if (nf->tru_h < 512 || (nf->tru_h > 576 && nf->tru_h < 1021))
1021*b285192aSMauro Carvalho Chehab 			nf->interlaced_y = 0;
1022*b285192aSMauro Carvalho Chehab 		else
1023*b285192aSMauro Carvalho Chehab 			nf->interlaced_y = 1;
1024*b285192aSMauro Carvalho Chehab 
1025*b285192aSMauro Carvalho Chehab 		if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2))
1026*b285192aSMauro Carvalho Chehab 			nf->interlaced_uv = 0;
1027*b285192aSMauro Carvalho Chehab 		else
1028*b285192aSMauro Carvalho Chehab 			nf->interlaced_uv = 1;
1029*b285192aSMauro Carvalho Chehab 		break;
1030*b285192aSMauro Carvalho Chehab 
1031*b285192aSMauro Carvalho Chehab 	case IVTV_YUV_MODE_AUTO:
1032*b285192aSMauro Carvalho Chehab 		if (nf->tru_h <= lace_threshold || nf->tru_h > 576 || nf->tru_w > 720) {
1033*b285192aSMauro Carvalho Chehab 			nf->interlaced = 0;
1034*b285192aSMauro Carvalho Chehab 			if ((nf->tru_h < 512) ||
1035*b285192aSMauro Carvalho Chehab 			    (nf->tru_h > 576 && nf->tru_h < 1021) ||
1036*b285192aSMauro Carvalho Chehab 			    (nf->tru_w > 720 && nf->tru_h < 1021))
1037*b285192aSMauro Carvalho Chehab 				nf->interlaced_y = 0;
1038*b285192aSMauro Carvalho Chehab 			else
1039*b285192aSMauro Carvalho Chehab 				nf->interlaced_y = 1;
1040*b285192aSMauro Carvalho Chehab 			if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2))
1041*b285192aSMauro Carvalho Chehab 				nf->interlaced_uv = 0;
1042*b285192aSMauro Carvalho Chehab 			else
1043*b285192aSMauro Carvalho Chehab 				nf->interlaced_uv = 1;
1044*b285192aSMauro Carvalho Chehab 		} else {
1045*b285192aSMauro Carvalho Chehab 			nf->interlaced = 1;
1046*b285192aSMauro Carvalho Chehab 			nf->interlaced_y = 1;
1047*b285192aSMauro Carvalho Chehab 			nf->interlaced_uv = 1;
1048*b285192aSMauro Carvalho Chehab 		}
1049*b285192aSMauro Carvalho Chehab 		break;
1050*b285192aSMauro Carvalho Chehab 
1051*b285192aSMauro Carvalho Chehab 	case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */
1052*b285192aSMauro Carvalho Chehab 	default:
1053*b285192aSMauro Carvalho Chehab 		nf->interlaced = 1;
1054*b285192aSMauro Carvalho Chehab 		nf->interlaced_y = 1;
1055*b285192aSMauro Carvalho Chehab 		nf->interlaced_uv = 1;
1056*b285192aSMauro Carvalho Chehab 		break;
1057*b285192aSMauro Carvalho Chehab 	}
1058*b285192aSMauro Carvalho Chehab 
1059*b285192aSMauro Carvalho Chehab 	if (memcmp(&yi->old_frame_info_args, nf, sizeof(*nf))) {
1060*b285192aSMauro Carvalho Chehab 		yi->old_frame_info_args = *nf;
1061*b285192aSMauro Carvalho Chehab 		nf->update = 1;
1062*b285192aSMauro Carvalho Chehab 		IVTV_DEBUG_YUV("Requesting reg update for frame %d\n", frame);
1063*b285192aSMauro Carvalho Chehab 	}
1064*b285192aSMauro Carvalho Chehab 
1065*b285192aSMauro Carvalho Chehab 	nf->update |= update;
1066*b285192aSMauro Carvalho Chehab 	nf->sync_field = yi->lace_sync_field;
1067*b285192aSMauro Carvalho Chehab 	nf->delay = nf->sync_field != of->sync_field;
1068*b285192aSMauro Carvalho Chehab }
1069*b285192aSMauro Carvalho Chehab 
1070*b285192aSMauro Carvalho Chehab /* Frame is complete & ready for display */
1071*b285192aSMauro Carvalho Chehab void ivtv_yuv_frame_complete(struct ivtv *itv)
1072*b285192aSMauro Carvalho Chehab {
1073*b285192aSMauro Carvalho Chehab 	atomic_set(&itv->yuv_info.next_fill_frame,
1074*b285192aSMauro Carvalho Chehab 			(itv->yuv_info.draw_frame + 1) % IVTV_YUV_BUFFERS);
1075*b285192aSMauro Carvalho Chehab }
1076*b285192aSMauro Carvalho Chehab 
1077*b285192aSMauro Carvalho Chehab static int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
1078*b285192aSMauro Carvalho Chehab {
1079*b285192aSMauro Carvalho Chehab 	DEFINE_WAIT(wait);
1080*b285192aSMauro Carvalho Chehab 	int rc = 0;
1081*b285192aSMauro Carvalho Chehab 	int got_sig = 0;
1082*b285192aSMauro Carvalho Chehab 	/* DMA the frame */
1083*b285192aSMauro Carvalho Chehab 	mutex_lock(&itv->udma.lock);
1084*b285192aSMauro Carvalho Chehab 
1085*b285192aSMauro Carvalho Chehab 	if ((rc = ivtv_yuv_prep_user_dma(itv, &itv->udma, args)) != 0) {
1086*b285192aSMauro Carvalho Chehab 		mutex_unlock(&itv->udma.lock);
1087*b285192aSMauro Carvalho Chehab 		return rc;
1088*b285192aSMauro Carvalho Chehab 	}
1089*b285192aSMauro Carvalho Chehab 
1090*b285192aSMauro Carvalho Chehab 	ivtv_udma_prepare(itv);
1091*b285192aSMauro Carvalho Chehab 	prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
1092*b285192aSMauro Carvalho Chehab 	/* if no UDMA is pending and no UDMA is in progress, then the DMA
1093*b285192aSMauro Carvalho Chehab 	   is finished */
1094*b285192aSMauro Carvalho Chehab 	while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) ||
1095*b285192aSMauro Carvalho Chehab 	       test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
1096*b285192aSMauro Carvalho Chehab 		/* don't interrupt if the DMA is in progress but break off
1097*b285192aSMauro Carvalho Chehab 		   a still pending DMA. */
1098*b285192aSMauro Carvalho Chehab 		got_sig = signal_pending(current);
1099*b285192aSMauro Carvalho Chehab 		if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
1100*b285192aSMauro Carvalho Chehab 			break;
1101*b285192aSMauro Carvalho Chehab 		got_sig = 0;
1102*b285192aSMauro Carvalho Chehab 		schedule();
1103*b285192aSMauro Carvalho Chehab 	}
1104*b285192aSMauro Carvalho Chehab 	finish_wait(&itv->dma_waitq, &wait);
1105*b285192aSMauro Carvalho Chehab 
1106*b285192aSMauro Carvalho Chehab 	/* Unmap Last DMA Xfer */
1107*b285192aSMauro Carvalho Chehab 	ivtv_udma_unmap(itv);
1108*b285192aSMauro Carvalho Chehab 
1109*b285192aSMauro Carvalho Chehab 	if (got_sig) {
1110*b285192aSMauro Carvalho Chehab 		IVTV_DEBUG_INFO("User stopped YUV UDMA\n");
1111*b285192aSMauro Carvalho Chehab 		mutex_unlock(&itv->udma.lock);
1112*b285192aSMauro Carvalho Chehab 		return -EINTR;
1113*b285192aSMauro Carvalho Chehab 	}
1114*b285192aSMauro Carvalho Chehab 
1115*b285192aSMauro Carvalho Chehab 	ivtv_yuv_frame_complete(itv);
1116*b285192aSMauro Carvalho Chehab 
1117*b285192aSMauro Carvalho Chehab 	mutex_unlock(&itv->udma.lock);
1118*b285192aSMauro Carvalho Chehab 	return rc;
1119*b285192aSMauro Carvalho Chehab }
1120*b285192aSMauro Carvalho Chehab 
1121*b285192aSMauro Carvalho Chehab /* Setup frame according to V4L2 parameters */
1122*b285192aSMauro Carvalho Chehab void ivtv_yuv_setup_stream_frame(struct ivtv *itv)
1123*b285192aSMauro Carvalho Chehab {
1124*b285192aSMauro Carvalho Chehab 	struct yuv_playback_info *yi = &itv->yuv_info;
1125*b285192aSMauro Carvalho Chehab 	struct ivtv_dma_frame dma_args;
1126*b285192aSMauro Carvalho Chehab 
1127*b285192aSMauro Carvalho Chehab 	ivtv_yuv_next_free(itv);
1128*b285192aSMauro Carvalho Chehab 
1129*b285192aSMauro Carvalho Chehab 	/* Copy V4L2 parameters to an ivtv_dma_frame struct... */
1130*b285192aSMauro Carvalho Chehab 	dma_args.y_source = NULL;
1131*b285192aSMauro Carvalho Chehab 	dma_args.uv_source = NULL;
1132*b285192aSMauro Carvalho Chehab 	dma_args.src.left = 0;
1133*b285192aSMauro Carvalho Chehab 	dma_args.src.top = 0;
1134*b285192aSMauro Carvalho Chehab 	dma_args.src.width = yi->v4l2_src_w;
1135*b285192aSMauro Carvalho Chehab 	dma_args.src.height = yi->v4l2_src_h;
1136*b285192aSMauro Carvalho Chehab 	dma_args.dst = yi->main_rect;
1137*b285192aSMauro Carvalho Chehab 	dma_args.src_width = yi->v4l2_src_w;
1138*b285192aSMauro Carvalho Chehab 	dma_args.src_height = yi->v4l2_src_h;
1139*b285192aSMauro Carvalho Chehab 
1140*b285192aSMauro Carvalho Chehab 	/* ... and use the same setup routine as ivtv_yuv_prep_frame */
1141*b285192aSMauro Carvalho Chehab 	ivtv_yuv_setup_frame(itv, &dma_args);
1142*b285192aSMauro Carvalho Chehab 
1143*b285192aSMauro Carvalho Chehab 	if (!itv->dma_data_req_offset)
1144*b285192aSMauro Carvalho Chehab 		itv->dma_data_req_offset = yuv_offset[yi->draw_frame];
1145*b285192aSMauro Carvalho Chehab }
1146*b285192aSMauro Carvalho Chehab 
1147*b285192aSMauro Carvalho Chehab /* Attempt to dma a frame from a user buffer */
1148*b285192aSMauro Carvalho Chehab int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void __user *src)
1149*b285192aSMauro Carvalho Chehab {
1150*b285192aSMauro Carvalho Chehab 	struct yuv_playback_info *yi = &itv->yuv_info;
1151*b285192aSMauro Carvalho Chehab 	struct ivtv_dma_frame dma_args;
1152*b285192aSMauro Carvalho Chehab 	int res;
1153*b285192aSMauro Carvalho Chehab 
1154*b285192aSMauro Carvalho Chehab 	ivtv_yuv_setup_stream_frame(itv);
1155*b285192aSMauro Carvalho Chehab 
1156*b285192aSMauro Carvalho Chehab 	/* We only need to supply source addresses for this */
1157*b285192aSMauro Carvalho Chehab 	dma_args.y_source = src;
1158*b285192aSMauro Carvalho Chehab 	dma_args.uv_source = src + 720 * ((yi->v4l2_src_h + 31) & ~31);
1159*b285192aSMauro Carvalho Chehab 	/* Wait for frame DMA. Note that serialize_lock is locked,
1160*b285192aSMauro Carvalho Chehab 	   so to allow other processes to access the driver while
1161*b285192aSMauro Carvalho Chehab 	   we are waiting unlock first and later lock again. */
1162*b285192aSMauro Carvalho Chehab 	mutex_unlock(&itv->serialize_lock);
1163*b285192aSMauro Carvalho Chehab 	res = ivtv_yuv_udma_frame(itv, &dma_args);
1164*b285192aSMauro Carvalho Chehab 	mutex_lock(&itv->serialize_lock);
1165*b285192aSMauro Carvalho Chehab 	return res;
1166*b285192aSMauro Carvalho Chehab }
1167*b285192aSMauro Carvalho Chehab 
1168*b285192aSMauro Carvalho Chehab /* IVTV_IOC_DMA_FRAME ioctl handler */
1169*b285192aSMauro Carvalho Chehab int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
1170*b285192aSMauro Carvalho Chehab {
1171*b285192aSMauro Carvalho Chehab 	int res;
1172*b285192aSMauro Carvalho Chehab 
1173*b285192aSMauro Carvalho Chehab /*	IVTV_DEBUG_INFO("yuv_prep_frame\n"); */
1174*b285192aSMauro Carvalho Chehab 	ivtv_yuv_next_free(itv);
1175*b285192aSMauro Carvalho Chehab 	ivtv_yuv_setup_frame(itv, args);
1176*b285192aSMauro Carvalho Chehab 	/* Wait for frame DMA. Note that serialize_lock is locked,
1177*b285192aSMauro Carvalho Chehab 	   so to allow other processes to access the driver while
1178*b285192aSMauro Carvalho Chehab 	   we are waiting unlock first and later lock again. */
1179*b285192aSMauro Carvalho Chehab 	mutex_unlock(&itv->serialize_lock);
1180*b285192aSMauro Carvalho Chehab 	res = ivtv_yuv_udma_frame(itv, args);
1181*b285192aSMauro Carvalho Chehab 	mutex_lock(&itv->serialize_lock);
1182*b285192aSMauro Carvalho Chehab 	return res;
1183*b285192aSMauro Carvalho Chehab }
1184*b285192aSMauro Carvalho Chehab 
1185*b285192aSMauro Carvalho Chehab void ivtv_yuv_close(struct ivtv *itv)
1186*b285192aSMauro Carvalho Chehab {
1187*b285192aSMauro Carvalho Chehab 	struct yuv_playback_info *yi = &itv->yuv_info;
1188*b285192aSMauro Carvalho Chehab 	int h_filter, v_filter_1, v_filter_2;
1189*b285192aSMauro Carvalho Chehab 
1190*b285192aSMauro Carvalho Chehab 	IVTV_DEBUG_YUV("ivtv_yuv_close\n");
1191*b285192aSMauro Carvalho Chehab 	mutex_unlock(&itv->serialize_lock);
1192*b285192aSMauro Carvalho Chehab 	ivtv_waitq(&itv->vsync_waitq);
1193*b285192aSMauro Carvalho Chehab 	mutex_lock(&itv->serialize_lock);
1194*b285192aSMauro Carvalho Chehab 
1195*b285192aSMauro Carvalho Chehab 	yi->running = 0;
1196*b285192aSMauro Carvalho Chehab 	atomic_set(&yi->next_dma_frame, -1);
1197*b285192aSMauro Carvalho Chehab 	atomic_set(&yi->next_fill_frame, 0);
1198*b285192aSMauro Carvalho Chehab 
1199*b285192aSMauro Carvalho Chehab 	/* Reset registers we have changed so mpeg playback works */
1200*b285192aSMauro Carvalho Chehab 
1201*b285192aSMauro Carvalho Chehab 	/* If we fully restore this register, the display may remain active.
1202*b285192aSMauro Carvalho Chehab 	   Restore, but set one bit to blank the video. Firmware will always
1203*b285192aSMauro Carvalho Chehab 	   clear this bit when needed, so not a problem. */
1204*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2898 | 0x01000000, 0x2898);
1205*b285192aSMauro Carvalho Chehab 
1206*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2834, 0x02834);
1207*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2838, 0x02838);
1208*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_283c, 0x0283c);
1209*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2840, 0x02840);
1210*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2844, 0x02844);
1211*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2848, 0x02848);
1212*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2854, 0x02854);
1213*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_285c, 0x0285c);
1214*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2864, 0x02864);
1215*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2870, 0x02870);
1216*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2874, 0x02874);
1217*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2890, 0x02890);
1218*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_289c, 0x0289c);
1219*b285192aSMauro Carvalho Chehab 
1220*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2918, 0x02918);
1221*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_291c, 0x0291c);
1222*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2920, 0x02920);
1223*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2924, 0x02924);
1224*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2928, 0x02928);
1225*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_292c, 0x0292c);
1226*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2930, 0x02930);
1227*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2934, 0x02934);
1228*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2938, 0x02938);
1229*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_293c, 0x0293c);
1230*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2940, 0x02940);
1231*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2944, 0x02944);
1232*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2948, 0x02948);
1233*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_294c, 0x0294c);
1234*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2950, 0x02950);
1235*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2954, 0x02954);
1236*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2958, 0x02958);
1237*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_295c, 0x0295c);
1238*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2960, 0x02960);
1239*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2964, 0x02964);
1240*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2968, 0x02968);
1241*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_296c, 0x0296c);
1242*b285192aSMauro Carvalho Chehab 	write_reg(yi->reg_2970, 0x02970);
1243*b285192aSMauro Carvalho Chehab 
1244*b285192aSMauro Carvalho Chehab 	/* Prepare to restore filters */
1245*b285192aSMauro Carvalho Chehab 
1246*b285192aSMauro Carvalho Chehab 	/* First the horizontal filter */
1247*b285192aSMauro Carvalho Chehab 	if ((yi->reg_2834 & 0x0000FFFF) == (yi->reg_2834 >> 16)) {
1248*b285192aSMauro Carvalho Chehab 		/* An exact size match uses filter 0 */
1249*b285192aSMauro Carvalho Chehab 		h_filter = 0;
1250*b285192aSMauro Carvalho Chehab 	} else {
1251*b285192aSMauro Carvalho Chehab 		/* Figure out which filter to use */
1252*b285192aSMauro Carvalho Chehab 		h_filter = ((yi->reg_2834 << 16) / (yi->reg_2834 >> 16)) >> 15;
1253*b285192aSMauro Carvalho Chehab 		h_filter = (h_filter >> 1) + (h_filter & 1);
1254*b285192aSMauro Carvalho Chehab 		/* Only an exact size match can use filter 0. */
1255*b285192aSMauro Carvalho Chehab 		h_filter += !h_filter;
1256*b285192aSMauro Carvalho Chehab 	}
1257*b285192aSMauro Carvalho Chehab 
1258*b285192aSMauro Carvalho Chehab 	/* Now the vertical filter */
1259*b285192aSMauro Carvalho Chehab 	if ((yi->reg_2918 & 0x0000FFFF) == (yi->reg_2918 >> 16)) {
1260*b285192aSMauro Carvalho Chehab 		/* An exact size match uses filter 0/1 */
1261*b285192aSMauro Carvalho Chehab 		v_filter_1 = 0;
1262*b285192aSMauro Carvalho Chehab 		v_filter_2 = 1;
1263*b285192aSMauro Carvalho Chehab 	} else {
1264*b285192aSMauro Carvalho Chehab 		/* Figure out which filter to use */
1265*b285192aSMauro Carvalho Chehab 		v_filter_1 = ((yi->reg_2918 << 16) / (yi->reg_2918 >> 16)) >> 15;
1266*b285192aSMauro Carvalho Chehab 		v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
1267*b285192aSMauro Carvalho Chehab 		/* Only an exact size match can use filter 0 */
1268*b285192aSMauro Carvalho Chehab 		v_filter_1 += !v_filter_1;
1269*b285192aSMauro Carvalho Chehab 		v_filter_2 = v_filter_1;
1270*b285192aSMauro Carvalho Chehab 	}
1271*b285192aSMauro Carvalho Chehab 
1272*b285192aSMauro Carvalho Chehab 	/* Now restore the filters */
1273*b285192aSMauro Carvalho Chehab 	ivtv_yuv_filter(itv, h_filter, v_filter_1, v_filter_2);
1274*b285192aSMauro Carvalho Chehab 
1275*b285192aSMauro Carvalho Chehab 	/* and clear a few registers */
1276*b285192aSMauro Carvalho Chehab 	write_reg(0, 0x02814);
1277*b285192aSMauro Carvalho Chehab 	write_reg(0, 0x0282c);
1278*b285192aSMauro Carvalho Chehab 	write_reg(0, 0x02904);
1279*b285192aSMauro Carvalho Chehab 	write_reg(0, 0x02910);
1280*b285192aSMauro Carvalho Chehab 
1281*b285192aSMauro Carvalho Chehab 	/* Release the blanking buffer */
1282*b285192aSMauro Carvalho Chehab 	if (yi->blanking_ptr) {
1283*b285192aSMauro Carvalho Chehab 		kfree(yi->blanking_ptr);
1284*b285192aSMauro Carvalho Chehab 		yi->blanking_ptr = NULL;
1285*b285192aSMauro Carvalho Chehab 		pci_unmap_single(itv->pdev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
1286*b285192aSMauro Carvalho Chehab 	}
1287*b285192aSMauro Carvalho Chehab 
1288*b285192aSMauro Carvalho Chehab 	/* Invalidate the old dimension information */
1289*b285192aSMauro Carvalho Chehab 	yi->old_frame_info.src_w = 0;
1290*b285192aSMauro Carvalho Chehab 	yi->old_frame_info.src_h = 0;
1291*b285192aSMauro Carvalho Chehab 	yi->old_frame_info_args.src_w = 0;
1292*b285192aSMauro Carvalho Chehab 	yi->old_frame_info_args.src_h = 0;
1293*b285192aSMauro Carvalho Chehab 
1294*b285192aSMauro Carvalho Chehab 	/* All done. */
1295*b285192aSMauro Carvalho Chehab 	clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
1296*b285192aSMauro Carvalho Chehab }
1297