/* 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); /* *Inheritthe-EFAULTfromrc's *initialization,butallowittobe *overriddenbyuv_pagesaboveifitwasan *actualerrno.
*/
} 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); return0;
}
/* 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;
}
} return0;
}
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);
}
}
/* 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, butifweuseit,itfoulsthewindowalignment.
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 inducingsotrytoreduceitslightly.Leave2968as-isotherwise
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 thesource.Checkagain,butthistimejustresize.Don'tchange
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, sotoallowotherprocessestoaccessthedriverwhile
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, sotoallowotherprocessestoaccessthedriverwhile
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,butsetonebittoblankthevideo.Firmwarewillalways
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.