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