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