brintos

brintos / linux-shallow public Read only

0
0
Text · 11.1 KiB · 0cfa016 Raw
420 lines · c
1/*2 * omap_vout_vrfb.c3 *4 * Copyright (C) 2010 Texas Instruments.5 *6 * This file is licensed under the terms of the GNU General Public License7 * version 2. This program is licensed "as is" without any warranty of any8 * kind, whether express or implied.9 *10 */11 12#include <linux/sched.h>13#include <linux/platform_device.h>14#include <linux/videodev2.h>15#include <linux/slab.h>16 17#include <media/v4l2-device.h>18 19#include <video/omapvrfb.h>20 21#include "omap_voutdef.h"22#include "omap_voutlib.h"23#include "omap_vout_vrfb.h"24 25#define OMAP_DMA_NO_DEVICE	026 27/*28 * Function for allocating video buffers29 */30static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout,31		unsigned int *count, int startindex)32{33	int i, j;34 35	for (i = 0; i < *count; i++) {36		if (!vout->smsshado_virt_addr[i]) {37			vout->smsshado_virt_addr[i] =38				omap_vout_alloc_buffer(vout->smsshado_size,39						&vout->smsshado_phy_addr[i]);40		}41		if (!vout->smsshado_virt_addr[i] && startindex != -1) {42			if (vout->vq.memory == V4L2_MEMORY_MMAP && i >= startindex)43				break;44		}45		if (!vout->smsshado_virt_addr[i]) {46			for (j = 0; j < i; j++) {47				omap_vout_free_buffer(48						vout->smsshado_virt_addr[j],49						vout->smsshado_size);50				vout->smsshado_virt_addr[j] = 0;51				vout->smsshado_phy_addr[j] = 0;52			}53			*count = 0;54			return -ENOMEM;55		}56		memset((void *)(long)vout->smsshado_virt_addr[i], 0,57		       vout->smsshado_size);58	}59	return 0;60}61 62/*63 * Wakes up the application once the DMA transfer to VRFB space is completed.64 */65static void omap_vout_vrfb_dma_tx_callback(void *data)66{67	struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data;68 69	t->tx_status = 1;70	wake_up_interruptible(&t->wait);71}72 73/*74 * Free VRFB buffers75 */76void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout)77{78	int j;79 80	for (j = 0; j < VRFB_NUM_BUFS; j++) {81		if (vout->smsshado_virt_addr[j]) {82			omap_vout_free_buffer(vout->smsshado_virt_addr[j],83					      vout->smsshado_size);84			vout->smsshado_virt_addr[j] = 0;85			vout->smsshado_phy_addr[j] = 0;86		}87	}88}89 90int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,91			      bool static_vrfb_allocation)92{93	int ret = 0, i, j;94	struct omap_vout_device *vout;95	struct video_device *vfd;96	dma_cap_mask_t mask;97	int image_width, image_height;98	int vrfb_num_bufs = VRFB_NUM_BUFS;99	struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);100	struct omap2video_device *vid_dev =101		container_of(v4l2_dev, struct omap2video_device, v4l2_dev);102 103	vout = vid_dev->vouts[vid_num];104	vfd = vout->vfd;105 106	for (i = 0; i < VRFB_NUM_BUFS; i++) {107		if (omap_vrfb_request_ctx(&vout->vrfb_context[i])) {108			dev_info(&pdev->dev, ": VRFB allocation failed\n");109			for (j = 0; j < i; j++)110				omap_vrfb_release_ctx(&vout->vrfb_context[j]);111			return -ENOMEM;112		}113	}114 115	/* Calculate VRFB memory size */116	/* allocate for worst case size */117	image_width = VID_MAX_WIDTH / TILE_SIZE;118	if (VID_MAX_WIDTH % TILE_SIZE)119		image_width++;120 121	image_width = image_width * TILE_SIZE;122	image_height = VID_MAX_HEIGHT / TILE_SIZE;123 124	if (VID_MAX_HEIGHT % TILE_SIZE)125		image_height++;126 127	image_height = image_height * TILE_SIZE;128	vout->smsshado_size = PAGE_ALIGN(image_width * image_height * 2 * 2);129 130	/*131	 * Request and Initialize DMA, for DMA based VRFB transfer132	 */133	dma_cap_zero(mask);134	dma_cap_set(DMA_INTERLEAVE, mask);135	vout->vrfb_dma_tx.chan = dma_request_chan_by_mask(&mask);136	if (IS_ERR(vout->vrfb_dma_tx.chan)) {137		vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;138	} else {139		size_t xt_size = sizeof(struct dma_interleaved_template) +140				 sizeof(struct data_chunk);141 142		vout->vrfb_dma_tx.xt = kzalloc(xt_size, GFP_KERNEL);143		if (!vout->vrfb_dma_tx.xt) {144			dma_release_channel(vout->vrfb_dma_tx.chan);145			vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;146		}147	}148 149	if (vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED)150		dev_info(&pdev->dev,151			 ": failed to allocate DMA Channel for video%d\n",152			 vfd->minor);153 154	init_waitqueue_head(&vout->vrfb_dma_tx.wait);155 156	/*157	 * statically allocated the VRFB buffer is done through158	 * command line arguments159	 */160	if (static_vrfb_allocation) {161		if (omap_vout_allocate_vrfb_buffers(vout, &vrfb_num_bufs, -1)) {162			ret =  -ENOMEM;163			goto release_vrfb_ctx;164		}165		vout->vrfb_static_allocation = true;166	}167	return 0;168 169release_vrfb_ctx:170	for (j = 0; j < VRFB_NUM_BUFS; j++)171		omap_vrfb_release_ctx(&vout->vrfb_context[j]);172	return ret;173}174 175/*176 * Release the VRFB context once the module exits177 */178void omap_vout_release_vrfb(struct omap_vout_device *vout)179{180	int i;181 182	for (i = 0; i < VRFB_NUM_BUFS; i++)183		omap_vrfb_release_ctx(&vout->vrfb_context[i]);184 185	if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) {186		vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;187		kfree(vout->vrfb_dma_tx.xt);188		dmaengine_terminate_sync(vout->vrfb_dma_tx.chan);189		dma_release_channel(vout->vrfb_dma_tx.chan);190	}191}192 193/*194 * Allocate the buffers for the VRFB space.  Data is copied from V4L2195 * buffers to the VRFB buffers using the DMA engine.196 */197int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,198			  unsigned int *count, unsigned int startindex)199{200	int i;201	bool yuv_mode;202 203	if (!is_rotation_enabled(vout))204		return 0;205 206	/* If rotation is enabled, allocate memory for VRFB space also */207	*count = *count > VRFB_NUM_BUFS ? VRFB_NUM_BUFS : *count;208 209	/* Allocate the VRFB buffers only if the buffers are not210	 * allocated during init time.211	 */212	if (!vout->vrfb_static_allocation)213		if (omap_vout_allocate_vrfb_buffers(vout, count, startindex))214			return -ENOMEM;215 216	if (vout->dss_mode == OMAP_DSS_COLOR_YUV2 ||217			vout->dss_mode == OMAP_DSS_COLOR_UYVY)218		yuv_mode = true;219	else220		yuv_mode = false;221 222	for (i = 0; i < *count; i++)223		omap_vrfb_setup(&vout->vrfb_context[i],224				vout->smsshado_phy_addr[i], vout->pix.width,225				vout->pix.height, vout->bpp, yuv_mode);226 227	return 0;228}229 230int omap_vout_prepare_vrfb(struct omap_vout_device *vout,231			   struct vb2_buffer *vb)232{233	struct dma_async_tx_descriptor *tx;234	enum dma_ctrl_flags flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;235	struct dma_chan *chan = vout->vrfb_dma_tx.chan;236	struct dma_interleaved_template *xt = vout->vrfb_dma_tx.xt;237	dma_cookie_t cookie;238	dma_addr_t buf_phy_addr = vb2_dma_contig_plane_dma_addr(vb, 0);239	enum dma_status status;240	enum dss_rotation rotation;241	size_t dst_icg;242	u32 pixsize;243 244	if (!is_rotation_enabled(vout))245		return 0;246 247	/* If rotation is enabled, copy input buffer into VRFB248	 * memory space using DMA. We are copying input buffer249	 * into VRFB memory space of desired angle and DSS will250	 * read image VRFB memory for 0 degree angle251	 */252 253	pixsize = vout->bpp * vout->vrfb_bpp;254	dst_icg = MAX_PIXELS_PER_LINE * pixsize - vout->pix.width * vout->bpp;255 256	xt->src_start = buf_phy_addr;257	xt->dst_start = vout->vrfb_context[vb->index].paddr[0];258 259	xt->numf = vout->pix.height;260	xt->frame_size = 1;261	xt->sgl[0].size = vout->pix.width * vout->bpp;262	xt->sgl[0].icg = dst_icg;263 264	xt->dir = DMA_MEM_TO_MEM;265	xt->src_sgl = false;266	xt->src_inc = true;267	xt->dst_sgl = true;268	xt->dst_inc = true;269 270	tx = dmaengine_prep_interleaved_dma(chan, xt, flags);271	if (tx == NULL) {272		pr_err("%s: DMA interleaved prep error\n", __func__);273		return -EINVAL;274	}275 276	tx->callback = omap_vout_vrfb_dma_tx_callback;277	tx->callback_param = &vout->vrfb_dma_tx;278 279	cookie = dmaengine_submit(tx);280	if (dma_submit_error(cookie)) {281		pr_err("%s: dmaengine_submit failed (%d)\n", __func__, cookie);282		return -EINVAL;283	}284 285	vout->vrfb_dma_tx.tx_status = 0;286	dma_async_issue_pending(chan);287 288	wait_event_interruptible_timeout(vout->vrfb_dma_tx.wait,289					 vout->vrfb_dma_tx.tx_status == 1,290					 VRFB_TX_TIMEOUT);291 292	status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);293 294	if (vout->vrfb_dma_tx.tx_status == 0) {295		pr_err("%s: Timeout while waiting for DMA\n", __func__);296		dmaengine_terminate_sync(chan);297		return -EINVAL;298	} else if (status != DMA_COMPLETE) {299		pr_err("%s: DMA completion %s status\n", __func__,300		       status == DMA_ERROR ? "error" : "busy");301		dmaengine_terminate_sync(chan);302		return -EINVAL;303	}304 305	/* Store buffers physical address into an array. Addresses306	 * from this array will be used to configure DSS */307	rotation = calc_rotation(vout);308	vout->queued_buf_addr[vb->index] =309		vout->vrfb_context[vb->index].paddr[rotation];310	return 0;311}312 313/*314 * Calculate the buffer offsets from which the streaming should315 * start. This offset calculation is mainly required because of316 * the VRFB 32 pixels alignment with rotation.317 */318void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)319{320	enum dss_rotation rotation;321	bool mirroring = vout->mirror;322	struct v4l2_rect *crop = &vout->crop;323	struct v4l2_pix_format *pix = &vout->pix;324	int *cropped_offset = &vout->cropped_offset;325	int vr_ps = 1, ps = 2, temp_ps = 2;326	int offset = 0, ctop = 0, cleft = 0, line_length = 0;327 328	rotation = calc_rotation(vout);329 330	if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||331			V4L2_PIX_FMT_UYVY == pix->pixelformat) {332		if (is_rotation_enabled(vout)) {333			/*334			 * ps    - Actual pixel size for YUYV/UYVY for335			 *         VRFB/Mirroring is 4 bytes336			 * vr_ps - Virtually pixel size for YUYV/UYVY is337			 *         2 bytes338			 */339			ps = 4;340			vr_ps = 2;341		} else {342			ps = 2;	/* otherwise the pixel size is 2 byte */343		}344	} else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) {345		ps = 4;346	} else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) {347		ps = 3;348	}349	vout->ps = ps;350	vout->vr_ps = vr_ps;351 352	if (is_rotation_enabled(vout)) {353		line_length = MAX_PIXELS_PER_LINE;354		ctop = (pix->height - crop->height) - crop->top;355		cleft = (pix->width - crop->width) - crop->left;356	} else {357		line_length = pix->width;358	}359	vout->line_length = line_length;360	switch (rotation) {361	case dss_rotation_90_degree:362		offset = vout->vrfb_context[0].yoffset *363			vout->vrfb_context[0].bytespp;364		temp_ps = ps / vr_ps;365		if (!mirroring) {366			*cropped_offset = offset + line_length *367				temp_ps * cleft + crop->top * temp_ps;368		} else {369			*cropped_offset = offset + line_length * temp_ps *370				cleft + crop->top * temp_ps + (line_length *371				((crop->width / (vr_ps)) - 1) * ps);372		}373		break;374	case dss_rotation_180_degree:375		offset = ((MAX_PIXELS_PER_LINE * vout->vrfb_context[0].yoffset *376			vout->vrfb_context[0].bytespp) +377			(vout->vrfb_context[0].xoffset *378			vout->vrfb_context[0].bytespp));379		if (!mirroring) {380			*cropped_offset = offset + (line_length * ps * ctop) +381				(cleft / vr_ps) * ps;382 383		} else {384			*cropped_offset = offset + (line_length * ps * ctop) +385				(cleft / vr_ps) * ps + (line_length *386				(crop->height - 1) * ps);387		}388		break;389	case dss_rotation_270_degree:390		offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset *391			vout->vrfb_context[0].bytespp;392		temp_ps = ps / vr_ps;393		if (!mirroring) {394			*cropped_offset = offset + line_length *395			    temp_ps * crop->left + ctop * ps;396		} else {397			*cropped_offset = offset + line_length *398				temp_ps * crop->left + ctop * ps +399				(line_length * ((crop->width / vr_ps) - 1) *400				 ps);401		}402		break;403	case dss_rotation_0_degree:404		if (!mirroring) {405			*cropped_offset = (line_length * ps) *406				crop->top + (crop->left / vr_ps) * ps;407		} else {408			*cropped_offset = (line_length * ps) *409				crop->top + (crop->left / vr_ps) * ps +410				(line_length * (crop->height - 1) * ps);411		}412		break;413	default:414		*cropped_offset = (line_length * ps * crop->top) /415			vr_ps + (crop->left * ps) / vr_ps +416			((crop->width / vr_ps) - 1) * ps;417		break;418	}419}420