/* Still in USE */ if (dma->SG_length || dma->page_count) {
IVTV_DEBUG_WARN
("prep_user_dma: SG_length %d page_count %d still full?\n",
dma->SG_length, dma->page_count); return -EBUSY;
}
/* Pin user pages for DMA Xfer */
y_pages = pin_user_pages_unlocked(y_dma.uaddr,
y_dma.page_count, &dma->map[0], 0);
uv_pages = 0; /* silence gcc. value is set and consumed only if: */ if (y_pages == y_dma.page_count) {
uv_pages = pin_user_pages_unlocked(uv_dma.uaddr,
uv_dma.page_count, &dma->map[y_pages], 0);
}
if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) { int rc = -EFAULT;
if (y_pages == y_dma.page_count) {
IVTV_DEBUG_WARN
("failed to map uv user pages, returned %d expecting %d\n",
uv_pages, uv_dma.page_count);
if (uv_pages >= 0) {
unpin_user_pages(&dma->map[y_pages], uv_pages);
rc = -EFAULT;
} else {
rc = uv_pages;
}
} else {
IVTV_DEBUG_WARN
("failed to map y user pages, returned %d expecting %d\n",
y_pages, y_dma.page_count);
} if (y_pages >= 0) {
unpin_user_pages(dma->map, y_pages); /* * Inherit the -EFAULT from rc's * initialization, but allow it to be * overridden by uv_pages above if it was an * actual errno.
*/
} else {
rc = y_pages;
} return rc;
}
dma->page_count = y_pages + uv_pages;
/* Fill & map SG List */ if (ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0)) < 0) {
IVTV_DEBUG_WARN("could not allocate bounce buffers for highmem userspace buffers\n");
unpin_user_pages(dma->map, dma->page_count);
dma->page_count = 0; return -ENOMEM;
}
dma->SG_length = dma_map_sg(&itv->pdev->dev, dma->SGlist,
dma->page_count, DMA_TO_DEVICE); if (!dma->SG_length) {
IVTV_DEBUG_WARN("%s: DMA map error, SG_length is 0\n", __func__);
unpin_user_pages(dma->map, dma->page_count);
dma->page_count = 0; return -EINVAL;
}
/* Fill SG Array with new values */
ivtv_udma_fill_sg_array(dma, y_buffer_offset, uv_buffer_offset, y_size);
/* If we've offset the y plane, ensure top area is blanked */ if (f->offset_y && yi->blanking_ptr) {
dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16);
dma->SGarray[dma->SG_length].src = cpu_to_le32(yi->blanking_dmaptr);
dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DECODER_OFFSET + yuv_offset[frame]);
dma->SG_length++;
}
/* Tag SG Array with Interrupt Bit */
dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
ivtv_udma_sync_for_device(itv); return 0;
}
/* We rely on a table held in the firmware - Quick check. */ int ivtv_yuv_filter_check(struct ivtv *itv)
{ int i, y, uv;
for (i = 0, y = 16, uv = 4; i < 16; i++, y += 24, uv += 12) { if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + y) != i << 16) ||
(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + uv) != i << 16)) {
IVTV_WARN ("YUV filter table not found in firmware.\n"); return -1;
}
} return 0;
}
staticvoid ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2)
{
u32 i, line;
/* If any filter is -1, then don't update it */ if (h_filter > -1) { if (h_filter > 4)
h_filter = 4;
i = IVTV_YUV_HORIZONTAL_FILTER_OFFSET + (h_filter * 384); for (line = 0; line < 16; line++) {
write_reg(read_dec(i), 0x02804);
write_reg(read_dec(i), 0x0281c);
i += 4;
write_reg(read_dec(i), 0x02808);
write_reg(read_dec(i), 0x02820);
i += 4;
write_reg(read_dec(i), 0x0280c);
write_reg(read_dec(i), 0x02824);
i += 4;
write_reg(read_dec(i), 0x02810);
write_reg(read_dec(i), 0x02828);
i += 4;
write_reg(read_dec(i), 0x02814);
write_reg(read_dec(i), 0x0282c);
i += 8;
write_reg(0, 0x02818);
write_reg(0, 0x02830);
}
IVTV_DEBUG_YUV("h_filter -> %d\n", h_filter);
}
if (v_filter_1 > -1) { if (v_filter_1 > 4)
v_filter_1 = 4;
i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_1 * 192); for (line = 0; line < 16; line++) {
write_reg(read_dec(i), 0x02900);
i += 4;
write_reg(read_dec(i), 0x02904);
i += 8;
write_reg(0, 0x02908);
}
IVTV_DEBUG_YUV("v_filter_1 -> %d\n", v_filter_1);
}
if (v_filter_2 > -1) { if (v_filter_2 > 4)
v_filter_2 = 4;
i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_2 * 192); for (line = 0; line < 16; line++) {
write_reg(read_dec(i), 0x0290c);
i += 4;
write_reg(read_dec(i), 0x02910);
i += 8;
write_reg(0, 0x02914);
}
IVTV_DEBUG_YUV("v_filter_2 -> %d\n", v_filter_2);
}
}
/* How wide is the src image */
x_cutoff = f->src_w + f->src_x;
/* Set the display width */
reg_2834 = f->dst_w;
reg_2838 = reg_2834;
/* Set the display position */
reg_2890 = f->dst_x;
/* Index into the image horizontally */
reg_2870 = 0;
/* 2870 is normally fudged to align video coords with osd coords. If running full screen, it causes an unwanted left shift Remove the fudge if we almost fill the screen. Gradually adjust the offset to avoid the video 'snapping' left/right if it gets dragged through this region.
Only do this if osd is full width. */ if (f->vis_w == 720) { if ((f->tru_x - f->pan_x > -1) && (f->tru_x - f->pan_x <= 40) && (f->dst_w >= 680))
reg_2870 = 10 - (f->tru_x - f->pan_x) / 4; elseif ((f->tru_x - f->pan_x < 0) && (f->tru_x - f->pan_x >= -20) && (f->dst_w >= 660))
reg_2870 = (10 + (f->tru_x - f->pan_x) / 2);
/* We also need to factor in the scaling
(src_w - dst_w) / (src_w / 4) */ if (f->dst_w > f->src_w)
reg_2870_base = ((f->dst_w - f->src_w)<<16) / (f->src_w <<14); else
reg_2870_base = 0;
/* What is the source video being treated as... */
IVTV_DEBUG_WARN("Source video: %s\n",
f->interlaced ? "Interlaced" : "Progressive");
/* We offset into the image using two different index methods, so split
the y source coord into two parts. */ if (f->src_y < 8) {
src_minor_uv = f->src_y;
src_major_uv = 0;
} else {
src_minor_uv = 8;
src_major_uv = f->src_y - 8;
}
/* FIXME These registers change depending on scaled / unscaled output
We really need to work out what they should be */ if (f->src_h == f->dst_h) {
reg_2934 = 0x00020000;
reg_293c = 0x00100000;
reg_2944 = 0x00040000;
reg_294c = 0x000b0000;
} else {
reg_2934 = 0x00000FF0;
reg_293c = 0x00000FF0;
reg_2944 = 0x00000FF0;
reg_294c = 0x00000FF0;
}
/* The first line to be displayed */
reg_2950 = 0x00010000 + src_major_y; if (f->interlaced_y)
reg_2950 += 0x00010000;
reg_2954 = reg_2950 + 1;
/* How much of the source to decode.
Take into account the source offset */
reg_2960 = ((src_minor_y + f->src_h + src_major_y) - 1) |
(((src_minor_uv + f->src_h + src_major_uv - 1) & ~1) << 15);
/* Okay, we've wasted time working out the correct value, but if we use it, it fouls the window alignment.
Fudge it to what we want... */
reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16));
reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16));
/* Deviate further from what it should be. I find the flicker headache inducing so try to reduce it slightly. Leave 2968 as-is otherwise
colours foul. */ if ((reg_2964 != 0x00010001) && (f->dst_h / 2 <= f->src_h))
reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF) / 2);
if (!f->interlaced_y)
reg_2964 -= 0x00010001; if (!f->interlaced_uv)
reg_2968 -= 0x00010001;
/* Only update filter 1 if we really need to */ if (v_filter_1 != yi->v_filter_1) {
ivtv_yuv_filter(itv, -1, v_filter_1, -1);
yi->v_filter_1 = v_filter_1;
}
/* Only update filter 2 if we really need to */ if (v_filter_2 != yi->v_filter_2) {
ivtv_yuv_filter(itv, -1, -1, v_filter_2);
yi->v_filter_2 = v_filter_2;
}
}
/* Modify the supplied coordinate information to fit the visible osd area */ static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f)
{ struct yuv_frame_info *of = &itv->yuv_info.old_frame_info; int osd_crop;
u32 osd_scale;
u32 yuv_update = 0;
/* Sorry, but no negative coords for src */ if (f->src_x < 0)
f->src_x = 0; if (f->src_y < 0)
f->src_y = 0;
/* Can only reduce width down to 1/4 original size */ if ((osd_crop = f->src_w - 4 * f->dst_w) > 0) {
f->src_x += osd_crop / 2;
f->src_w = (f->src_w - osd_crop) & ~3;
f->dst_w = f->src_w / 4;
f->dst_w += f->dst_w & 1;
}
/* Can only reduce height down to 1/4 original size */ if (f->src_h / f->dst_h >= 2) { /* Overflow may be because we're running progressive,
so force mode switch */
f->interlaced_y = 1; /* Make sure we're still within limits for interlace */ if ((osd_crop = f->src_h - 4 * f->dst_h) > 0) { /* If we reach here we'll have to force the height. */
f->src_y += osd_crop / 2;
f->src_h = (f->src_h - osd_crop) & ~3;
f->dst_h = f->src_h / 4;
f->dst_h += f->dst_h & 1;
}
}
/* If there's nothing to safe to display, we may as well stop now */ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 ||
(int)f->src_w <= 2 || (int)f->src_h <= 2) { return IVTV_YUV_UPDATE_INVALID;
}
/* Ensure video remains inside OSD area */
osd_scale = (f->src_h << 16) / f->dst_h;
if ((osd_crop = f->pan_x - f->dst_x) > 0) { /* Fall off the left edge - crop */
f->src_x += (osd_scale * osd_crop) >> 16;
f->src_w -= (osd_scale * osd_crop) >> 16;
f->dst_w -= osd_crop;
f->dst_x = 0;
} else {
f->dst_x -= f->pan_x;
}
if ((osd_crop = f->dst_w + f->dst_x - f->vis_w) > 0) { /* Falls off the right edge - crop */
f->dst_w -= osd_crop;
f->src_w -= (osd_scale * osd_crop) >> 16;
}
if (itv->yuv_info.track_osd) { /* The OSD can be moved. Track to it */
f->dst_x += itv->yuv_info.osd_x_offset;
f->dst_y += itv->yuv_info.osd_y_offset;
}
/* Width & height for both src & dst must be even.
Same for coordinates. */
f->dst_w &= ~1;
f->dst_x &= ~1;
f->src_w += f->src_x & 1;
f->src_x &= ~1;
f->src_w &= ~1;
f->dst_w &= ~1;
f->dst_h &= ~1;
f->dst_y &= ~1;
f->src_h += f->src_y & 1;
f->src_y &= ~1;
f->src_h &= ~1;
f->dst_h &= ~1;
/* Due to rounding, we may have reduced the output size to <1/4 of the source. Check again, but this time just resize. Don't change
source coordinates */ if (f->dst_w < f->src_w / 4) {
f->src_w &= ~3;
f->dst_w = f->src_w / 4;
f->dst_w += f->dst_w & 1;
} if (f->dst_h < f->src_h / 4) {
f->src_h &= ~3;
f->dst_h = f->src_h / 4;
f->dst_h += f->dst_h & 1;
}
/* Check again. If there's nothing to safe to display, stop now */ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 ||
(int)f->src_w <= 2 || (int)f->src_h <= 2) { return IVTV_YUV_UPDATE_INVALID;
}
/* Both x offset & width are linked, so they have to be done together */ if ((of->dst_w != f->dst_w) || (of->src_w != f->src_w) ||
(of->dst_x != f->dst_x) || (of->src_x != f->src_x) ||
(of->pan_x != f->pan_x) || (of->vis_w != f->vis_w)) {
yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL;
}
/* Set some valid size info */
yi->osd_x_offset = read_reg(0x02a04) & 0x00000FFF;
yi->osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF;
/* Bit 2 of reg 2878 indicates current decoder output format
0 : NTSC 1 : PAL */ if (read_reg(0x2878) & 4)
yi->decode_height = 576; else
yi->decode_height = 480;
if (!itv->osd_info) {
yi->osd_vis_w = 720 - yi->osd_x_offset;
yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
} else { /* If no visible size set, assume full size */ if (!yi->osd_vis_w)
yi->osd_vis_w = 720 - yi->osd_x_offset;
if (!yi->osd_vis_h) {
yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
} elseif (yi->osd_vis_h + yi->osd_y_offset > yi->decode_height) { /* If output video standard has changed, requested height may
not be legal */
IVTV_DEBUG_WARN("Clipping yuv output - fb size (%d) exceeds video standard limit (%d)\n",
yi->osd_vis_h + yi->osd_y_offset,
yi->decode_height);
yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
}
}
/* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */
yi->blanking_ptr = kzalloc(720 * 16, GFP_ATOMIC|__GFP_NOWARN); if (yi->blanking_ptr) {
yi->blanking_dmaptr = dma_map_single(&itv->pdev->dev,
yi->blanking_ptr,
720 * 16, DMA_TO_DEVICE); if (dma_mapping_error(&itv->pdev->dev, yi->blanking_dmaptr)) {
kfree(yi->blanking_ptr);
yi->blanking_ptr = NULL;
yi->blanking_dmaptr = 0;
IVTV_DEBUG_WARN("Failed to dma_map yuv blanking buffer\n");
}
} else {
yi->blanking_dmaptr = 0;
IVTV_DEBUG_WARN("Failed to allocate yuv blanking buffer\n");
}
/* Get next available yuv buffer on PVR350 */ staticvoid ivtv_yuv_next_free(struct ivtv *itv)
{ int draw, display; struct yuv_playback_info *yi = &itv->yuv_info;
if (atomic_read(&yi->next_dma_frame) == -1)
ivtv_yuv_init(itv);
ivtv_udma_prepare(itv);
prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); /* if no UDMA is pending and no UDMA is in progress, then the DMA
is finished */ while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) ||
test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { /* don't interrupt if the DMA is in progress but break off
a still pending DMA. */
got_sig = signal_pending(current); if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) break;
got_sig = 0;
schedule();
}
finish_wait(&itv->dma_waitq, &wait);
/* ... and use the same setup routine as ivtv_yuv_prep_frame */
ivtv_yuv_setup_frame(itv, &dma_args);
if (!itv->dma_data_req_offset)
itv->dma_data_req_offset = yuv_offset[yi->draw_frame];
}
/* Attempt to dma a frame from a user buffer */ int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void __user *src)
{ struct yuv_playback_info *yi = &itv->yuv_info; struct ivtv_dma_frame dma_args; int res;
ivtv_yuv_setup_stream_frame(itv);
/* We only need to supply source addresses for this */
dma_args.y_source = src;
dma_args.uv_source = src + 720 * ((yi->v4l2_src_h + 31) & ~31); /* Wait for frame DMA. Note that serialize_lock is locked, so to allow other processes to access the driver while
we are waiting unlock first and later lock again. */
mutex_unlock(&itv->serialize_lock);
res = ivtv_yuv_udma_frame(itv, &dma_args);
mutex_lock(&itv->serialize_lock); return res;
}
/* IVTV_IOC_DMA_FRAME ioctl handler */ int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
{ int res;
/* IVTV_DEBUG_INFO("yuv_prep_frame\n"); */
ivtv_yuv_next_free(itv);
ivtv_yuv_setup_frame(itv, args); /* Wait for frame DMA. Note that serialize_lock is locked, so to allow other processes to access the driver while
we are waiting unlock first and later lock again. */
mutex_unlock(&itv->serialize_lock);
res = ivtv_yuv_udma_frame(itv, args);
mutex_lock(&itv->serialize_lock); return res;
}
/* Reset registers we have changed so mpeg playback works */
/* If we fully restore this register, the display may remain active. Restore, but set one bit to blank the video. Firmware will always
clear this bit when needed, so not a problem. */
write_reg(yi->reg_2898 | 0x01000000, 0x2898);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.