• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * omap_voutlib.c
3   *
4   * Copyright (C) 2005-2010 Texas Instruments.
5   *
6   * This file is licensed under the terms of the GNU General Public License
7   * version 2. This program is licensed "as is" without any warranty of any
8   * kind, whether express or implied.
9   *
10   * Based on the OMAP2 camera driver
11   * Video-for-Linux (Version 2) camera capture driver for
12   * the OMAP24xx camera controller.
13   *
14   * Author: Andy Lowe (source@mvista.com)
15   *
16   * Copyright (C) 2004 MontaVista Software, Inc.
17   * Copyright (C) 2010 Texas Instruments.
18   *
19   */
20  
21  #include <linux/module.h>
22  #include <linux/errno.h>
23  #include <linux/kernel.h>
24  #include <linux/types.h>
25  #include <linux/videodev2.h>
26  
27  #include <linux/dma-mapping.h>
28  
29  #include <video/omapfb_dss.h>
30  
31  #include "omap_voutlib.h"
32  
33  MODULE_AUTHOR("Texas Instruments");
34  MODULE_DESCRIPTION("OMAP Video library");
35  MODULE_LICENSE("GPL");
36  
37  /* Return the default overlay cropping rectangle in crop given the image
38   * size in pix and the video display size in fbuf.  The default
39   * cropping rectangle is the largest rectangle no larger than the capture size
40   * that will fit on the display.  The default cropping rectangle is centered in
41   * the image.  All dimensions and offsets are rounded down to even numbers.
42   */
omap_vout_default_crop(struct v4l2_pix_format * pix,struct v4l2_framebuffer * fbuf,struct v4l2_rect * crop)43  void omap_vout_default_crop(struct v4l2_pix_format *pix,
44  		  struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop)
45  {
46  	crop->width = (pix->width < fbuf->fmt.width) ?
47  		pix->width : fbuf->fmt.width;
48  	crop->height = (pix->height < fbuf->fmt.height) ?
49  		pix->height : fbuf->fmt.height;
50  	crop->width &= ~1;
51  	crop->height &= ~1;
52  	crop->left = ((pix->width - crop->width) >> 1) & ~1;
53  	crop->top = ((pix->height - crop->height) >> 1) & ~1;
54  }
55  EXPORT_SYMBOL_GPL(omap_vout_default_crop);
56  
57  /* Given a new render window in new_win, adjust the window to the
58   * nearest supported configuration.  The adjusted window parameters are
59   * returned in new_win.
60   * Returns zero if successful, or -EINVAL if the requested window is
61   * impossible and cannot reasonably be adjusted.
62   */
omap_vout_try_window(struct v4l2_framebuffer * fbuf,struct v4l2_window * new_win)63  int omap_vout_try_window(struct v4l2_framebuffer *fbuf,
64  			struct v4l2_window *new_win)
65  {
66  	struct v4l2_rect try_win;
67  
68  	/* make a working copy of the new_win rectangle */
69  	try_win = new_win->w;
70  
71  	/* adjust the preview window so it fits on the display by clipping any
72  	 * offscreen areas
73  	 */
74  	if (try_win.left < 0) {
75  		try_win.width += try_win.left;
76  		try_win.left = 0;
77  	}
78  	if (try_win.top < 0) {
79  		try_win.height += try_win.top;
80  		try_win.top = 0;
81  	}
82  	try_win.width = (try_win.width < fbuf->fmt.width) ?
83  		try_win.width : fbuf->fmt.width;
84  	try_win.height = (try_win.height < fbuf->fmt.height) ?
85  		try_win.height : fbuf->fmt.height;
86  	if (try_win.left + try_win.width > fbuf->fmt.width)
87  		try_win.width = fbuf->fmt.width - try_win.left;
88  	if (try_win.top + try_win.height > fbuf->fmt.height)
89  		try_win.height = fbuf->fmt.height - try_win.top;
90  	try_win.width &= ~1;
91  	try_win.height &= ~1;
92  
93  	if (try_win.width <= 0 || try_win.height <= 0)
94  		return -EINVAL;
95  
96  	/* We now have a valid preview window, so go with it */
97  	new_win->w = try_win;
98  	new_win->field = V4L2_FIELD_NONE;
99  	new_win->clips = NULL;
100  	new_win->clipcount = 0;
101  	new_win->bitmap = NULL;
102  
103  	return 0;
104  }
105  EXPORT_SYMBOL_GPL(omap_vout_try_window);
106  
107  /* Given a new render window in new_win, adjust the window to the
108   * nearest supported configuration.  The image cropping window in crop
109   * will also be adjusted if necessary.  Preference is given to keeping the
110   * the window as close to the requested configuration as possible.  If
111   * successful, new_win, vout->win, and crop are updated.
112   * Returns zero if successful, or -EINVAL if the requested preview window is
113   * impossible and cannot reasonably be adjusted.
114   */
omap_vout_new_window(struct v4l2_rect * crop,struct v4l2_window * win,struct v4l2_framebuffer * fbuf,struct v4l2_window * new_win)115  int omap_vout_new_window(struct v4l2_rect *crop,
116  		struct v4l2_window *win, struct v4l2_framebuffer *fbuf,
117  		struct v4l2_window *new_win)
118  {
119  	int err;
120  
121  	err = omap_vout_try_window(fbuf, new_win);
122  	if (err)
123  		return err;
124  
125  	/* update our preview window */
126  	win->w = new_win->w;
127  	win->field = new_win->field;
128  	win->chromakey = new_win->chromakey;
129  
130  	/* Adjust the cropping window to allow for resizing limitation */
131  	if (omap_vout_dss_omap24xx()) {
132  		/* For 24xx limit is 8x to 1/2x scaling. */
133  		if ((crop->height/win->w.height) >= 2)
134  			crop->height = win->w.height * 2;
135  
136  		if ((crop->width/win->w.width) >= 2)
137  			crop->width = win->w.width * 2;
138  
139  		if (crop->width > 768) {
140  			/* The OMAP2420 vertical resizing line buffer is 768
141  			 * pixels wide. If the cropped image is wider than
142  			 * 768 pixels then it cannot be vertically resized.
143  			 */
144  			if (crop->height != win->w.height)
145  				crop->width = 768;
146  		}
147  	} else if (omap_vout_dss_omap34xx()) {
148  		/* For 34xx limit is 8x to 1/4x scaling. */
149  		if ((crop->height/win->w.height) >= 4)
150  			crop->height = win->w.height * 4;
151  
152  		if ((crop->width/win->w.width) >= 4)
153  			crop->width = win->w.width * 4;
154  	}
155  	return 0;
156  }
157  EXPORT_SYMBOL_GPL(omap_vout_new_window);
158  
159  /* Given a new cropping rectangle in new_crop, adjust the cropping rectangle to
160   * the nearest supported configuration.  The image render window in win will
161   * also be adjusted if necessary.  The preview window is adjusted such that the
162   * horizontal and vertical rescaling ratios stay constant.  If the render
163   * window would fall outside the display boundaries, the cropping rectangle
164   * will also be adjusted to maintain the rescaling ratios.  If successful, crop
165   * and win are updated.
166   * Returns zero if successful, or -EINVAL if the requested cropping rectangle is
167   * impossible and cannot reasonably be adjusted.
168   */
omap_vout_new_crop(struct v4l2_pix_format * pix,struct v4l2_rect * crop,struct v4l2_window * win,struct v4l2_framebuffer * fbuf,const struct v4l2_rect * new_crop)169  int omap_vout_new_crop(struct v4l2_pix_format *pix,
170  	      struct v4l2_rect *crop, struct v4l2_window *win,
171  	      struct v4l2_framebuffer *fbuf, const struct v4l2_rect *new_crop)
172  {
173  	struct v4l2_rect try_crop;
174  	unsigned long vresize, hresize;
175  
176  	/* make a working copy of the new_crop rectangle */
177  	try_crop = *new_crop;
178  
179  	/* adjust the cropping rectangle so it fits in the image */
180  	if (try_crop.left < 0) {
181  		try_crop.width += try_crop.left;
182  		try_crop.left = 0;
183  	}
184  	if (try_crop.top < 0) {
185  		try_crop.height += try_crop.top;
186  		try_crop.top = 0;
187  	}
188  	try_crop.width = (try_crop.width < pix->width) ?
189  		try_crop.width : pix->width;
190  	try_crop.height = (try_crop.height < pix->height) ?
191  		try_crop.height : pix->height;
192  	if (try_crop.left + try_crop.width > pix->width)
193  		try_crop.width = pix->width - try_crop.left;
194  	if (try_crop.top + try_crop.height > pix->height)
195  		try_crop.height = pix->height - try_crop.top;
196  
197  	try_crop.width &= ~1;
198  	try_crop.height &= ~1;
199  
200  	if (try_crop.width <= 0 || try_crop.height <= 0)
201  		return -EINVAL;
202  
203  	if (omap_vout_dss_omap24xx()) {
204  		if (try_crop.height != win->w.height) {
205  			/* If we're resizing vertically, we can't support a
206  			 * crop width wider than 768 pixels.
207  			 */
208  			if (try_crop.width > 768)
209  				try_crop.width = 768;
210  		}
211  	}
212  	/* vertical resizing */
213  	vresize = (1024 * try_crop.height) / win->w.height;
214  	if (omap_vout_dss_omap24xx() && (vresize > 2048))
215  		vresize = 2048;
216  	else if (omap_vout_dss_omap34xx() && (vresize > 4096))
217  		vresize = 4096;
218  
219  	win->w.height = ((1024 * try_crop.height) / vresize) & ~1;
220  	if (win->w.height == 0)
221  		win->w.height = 2;
222  	if (win->w.height + win->w.top > fbuf->fmt.height) {
223  		/* We made the preview window extend below the bottom of the
224  		 * display, so clip it to the display boundary and resize the
225  		 * cropping height to maintain the vertical resizing ratio.
226  		 */
227  		win->w.height = (fbuf->fmt.height - win->w.top) & ~1;
228  		if (try_crop.height == 0)
229  			try_crop.height = 2;
230  	}
231  	/* horizontal resizing */
232  	hresize = (1024 * try_crop.width) / win->w.width;
233  	if (omap_vout_dss_omap24xx() && (hresize > 2048))
234  		hresize = 2048;
235  	else if (omap_vout_dss_omap34xx() && (hresize > 4096))
236  		hresize = 4096;
237  
238  	win->w.width = ((1024 * try_crop.width) / hresize) & ~1;
239  	if (win->w.width == 0)
240  		win->w.width = 2;
241  	if (win->w.width + win->w.left > fbuf->fmt.width) {
242  		/* We made the preview window extend past the right side of the
243  		 * display, so clip it to the display boundary and resize the
244  		 * cropping width to maintain the horizontal resizing ratio.
245  		 */
246  		win->w.width = (fbuf->fmt.width - win->w.left) & ~1;
247  		if (try_crop.width == 0)
248  			try_crop.width = 2;
249  	}
250  	if (omap_vout_dss_omap24xx()) {
251  		if ((try_crop.height/win->w.height) >= 2)
252  			try_crop.height = win->w.height * 2;
253  
254  		if ((try_crop.width/win->w.width) >= 2)
255  			try_crop.width = win->w.width * 2;
256  
257  		if (try_crop.width > 768) {
258  			/* The OMAP2420 vertical resizing line buffer is
259  			 * 768 pixels wide.  If the cropped image is wider
260  			 * than 768 pixels then it cannot be vertically resized.
261  			 */
262  			if (try_crop.height != win->w.height)
263  				try_crop.width = 768;
264  		}
265  	} else if (omap_vout_dss_omap34xx()) {
266  		if ((try_crop.height/win->w.height) >= 4)
267  			try_crop.height = win->w.height * 4;
268  
269  		if ((try_crop.width/win->w.width) >= 4)
270  			try_crop.width = win->w.width * 4;
271  	}
272  	/* update our cropping rectangle and we're done */
273  	*crop = try_crop;
274  	return 0;
275  }
276  EXPORT_SYMBOL_GPL(omap_vout_new_crop);
277  
278  /* Given a new format in pix and fbuf,  crop and win
279   * structures are initialized to default values. crop
280   * is initialized to the largest window size that will fit on the display.  The
281   * crop window is centered in the image. win is initialized to
282   * the same size as crop and is centered on the display.
283   * All sizes and offsets are constrained to be even numbers.
284   */
omap_vout_new_format(struct v4l2_pix_format * pix,struct v4l2_framebuffer * fbuf,struct v4l2_rect * crop,struct v4l2_window * win)285  void omap_vout_new_format(struct v4l2_pix_format *pix,
286  		struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop,
287  		struct v4l2_window *win)
288  {
289  	/* crop defines the preview source window in the image capture
290  	 * buffer
291  	 */
292  	omap_vout_default_crop(pix, fbuf, crop);
293  
294  	/* win defines the preview target window on the display */
295  	win->w.width = crop->width;
296  	win->w.height = crop->height;
297  	win->w.left = ((fbuf->fmt.width - win->w.width) >> 1) & ~1;
298  	win->w.top = ((fbuf->fmt.height - win->w.height) >> 1) & ~1;
299  }
300  EXPORT_SYMBOL_GPL(omap_vout_new_format);
301  
302  /*
303   * Allocate buffers
304   */
omap_vout_alloc_buffer(u32 buf_size,u32 * phys_addr)305  unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr)
306  {
307  	u32 order, size;
308  	unsigned long virt_addr, addr;
309  
310  	size = PAGE_ALIGN(buf_size);
311  	order = get_order(size);
312  	virt_addr = __get_free_pages(GFP_KERNEL, order);
313  	addr = virt_addr;
314  
315  	if (virt_addr) {
316  		while (size > 0) {
317  			SetPageReserved(virt_to_page(addr));
318  			addr += PAGE_SIZE;
319  			size -= PAGE_SIZE;
320  		}
321  	}
322  	*phys_addr = (u32) virt_to_phys((void *) virt_addr);
323  	return virt_addr;
324  }
325  
326  /*
327   * Free buffers
328   */
omap_vout_free_buffer(unsigned long virtaddr,u32 buf_size)329  void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size)
330  {
331  	u32 order, size;
332  	unsigned long addr = virtaddr;
333  
334  	size = PAGE_ALIGN(buf_size);
335  	order = get_order(size);
336  
337  	while (size > 0) {
338  		ClearPageReserved(virt_to_page(addr));
339  		addr += PAGE_SIZE;
340  		size -= PAGE_SIZE;
341  	}
342  	free_pages((unsigned long) virtaddr, order);
343  }
344  
omap_vout_dss_omap24xx(void)345  bool omap_vout_dss_omap24xx(void)
346  {
347  	return omapdss_get_version() == OMAPDSS_VER_OMAP24xx;
348  }
349  
omap_vout_dss_omap34xx(void)350  bool omap_vout_dss_omap34xx(void)
351  {
352  	switch (omapdss_get_version()) {
353  	case OMAPDSS_VER_OMAP34xx_ES1:
354  	case OMAPDSS_VER_OMAP34xx_ES3:
355  	case OMAPDSS_VER_OMAP3630:
356  	case OMAPDSS_VER_AM35xx:
357  		return true;
358  	default:
359  		return false;
360  	}
361  }
362