#define au0828_isocdbg(fmt, arg...) \ do {\ if (isoc_debug) { \
pr_info("au0828 %s :"fmt, \
__func__ , ##arg); \
} \
} while (0)
staticinlinevoid i2c_gate_ctrl(struct au0828_dev *dev, int val)
{ if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl)
dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, val);
}
staticinlinevoid print_err_status(struct au0828_dev *dev, int packet, int status)
{ char *errmsg = "Unknown";
switch (status) { case -ENOENT:
errmsg = "unlinked synchronously"; break; case -ECONNRESET:
errmsg = "unlinked asynchronously"; break; case -ENOSR:
errmsg = "Buffer error (overrun)"; break; case -EPIPE:
errmsg = "Stalled (device not responding)"; break; case -EOVERFLOW:
errmsg = "Babble (bad cable?)"; break; case -EPROTO:
errmsg = "Bit-stuff error (bad cable?)"; break; case -EILSEQ:
errmsg = "CRC/Timeout (could be anything)"; break; case -ETIME:
errmsg = "Device does not respond"; break;
} if (packet < 0) {
au0828_isocdbg("URB status %d [%s].\n", status, errmsg);
} else {
au0828_isocdbg("URB packet %d, status %d [%s].\n",
packet, status, errmsg);
}
}
staticint check_dev(struct au0828_dev *dev)
{ if (test_bit(DEV_DISCONNECTED, &dev->dev_state)) {
pr_info("v4l2 ioctl: device not present\n"); return -ENODEV;
}
if (test_bit(DEV_MISCONFIGURED, &dev->dev_state)) {
pr_info("v4l2 ioctl: device is misconfigured; close and open it again\n"); return -EIO;
} return 0;
}
/* * Stop and Deallocate URBs
*/ staticvoid au0828_uninit_isoc(struct au0828_dev *dev)
{ struct urb *urb; int i;
au0828_isocdbg("au0828: called au0828_uninit_isoc\n");
dev->isoc_ctl.nfields = -1; for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
urb = dev->isoc_ctl.urb[i]; if (urb) { if (!irqs_disabled())
usb_kill_urb(urb); else
usb_unlink_urb(urb);
/* * Allocate URBs and start IRQ
*/ staticint au0828_init_isoc(struct au0828_dev *dev, int max_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb))
{ struct au0828_dmaqueue *dma_q = &dev->vidq; int i; int sb_size, pipe; struct urb *urb; int j, k; int rc;
au0828_isocdbg("au0828: called au0828_prepare_isoc\n");
/* allocate urbs and transfer buffers */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
urb = usb_alloc_urb(max_packets, GFP_KERNEL); if (!urb) {
au0828_isocdbg("cannot allocate URB\n");
au0828_uninit_isoc(dev); return -ENOMEM;
}
dev->isoc_ctl.urb[i] = urb;
dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->usbdev,
sb_size, GFP_KERNEL, &urb->transfer_dma); if (!dev->isoc_ctl.transfer_buffer[i]) {
au0828_isocdbg("cannot allocate transfer buffer\n");
au0828_uninit_isoc(dev); return -ENOMEM;
}
memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
k = 0; for (j = 0; j < max_packets; j++) {
urb->iso_frame_desc[j].offset = k;
urb->iso_frame_desc[j].length =
dev->isoc_ctl.max_pkt_size;
k += dev->isoc_ctl.max_pkt_size;
}
}
/* submit urbs and enables IRQ */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); if (rc) {
au0828_isocdbg("submit of urb %i failed (error=%i)\n",
i, rc);
au0828_uninit_isoc(dev); return rc;
}
}
return 0;
}
/* * Announces that a buffer were filled and request the next
*/ staticinlinevoid buffer_filled(struct au0828_dev *dev, struct au0828_dmaqueue *dma_q, struct au0828_buffer *buf)
{ struct vb2_v4l2_buffer *vb = &buf->vb; struct vb2_queue *q = vb->vb2_buf.vb2_queue;
/* Advice that buffer was filled */
au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field);
if (offset > 1440) { /* We have enough data to check for greenscreen */ if (outp[0] < 0x60 && outp[1440] < 0x60)
dev->greenscreen_detected = 1;
}
dma_q->pos += len;
}
/* * generic routine to get the next available buffer
*/ staticinlinevoid get_next_buf(struct au0828_dmaqueue *dma_q, struct au0828_buffer **buf)
{ struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
if (list_empty(&dma_q->active)) {
au0828_isocdbg("No active queue to serve\n");
dev->isoc_ctl.buf = NULL;
*buf = NULL; return;
}
/* Get the next buffer */
*buf = list_entry(dma_q->active.next, struct au0828_buffer, list); /* Cleans up buffer - Useful for testing for frame/URB loss */
list_del(&(*buf)->list);
dma_q->pos = 0;
(*buf)->vb_buf = (*buf)->mem;
dev->isoc_ctl.buf = *buf;
return;
}
staticvoid au0828_copy_vbi(struct au0828_dev *dev, struct au0828_dmaqueue *dma_q, struct au0828_buffer *buf, unsignedchar *p, unsignedchar *outp, unsignedlong len)
{ unsignedchar *startwrite, *startread; int bytesperline; int i, j = 0;
if (dev == NULL) {
au0828_isocdbg("dev is null\n"); return;
}
if (dma_q == NULL) {
au0828_isocdbg("dma_q is null\n"); return;
} if (buf == NULL) return; if (p == NULL) {
au0828_isocdbg("p is null\n"); return;
} if (outp == NULL) {
au0828_isocdbg("outp is null\n"); return;
}
bytesperline = dev->vbi_width;
if (dma_q->pos + len > buf->length)
len = buf->length - dma_q->pos;
for (i = 0; i < urb->number_of_packets; i++) { int status = urb->iso_frame_desc[i].status;
if (status < 0) {
print_err_status(dev, i, status); if (urb->iso_frame_desc[i].status != -EPROTO) continue;
}
if (urb->iso_frame_desc[i].actual_length <= 0) continue;
if (urb->iso_frame_desc[i].actual_length >
dev->max_pkt_size) {
au0828_isocdbg("packet bigger than packet size"); continue;
}
p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
fbyte = p[0];
len = urb->iso_frame_desc[i].actual_length - 4;
p += 4;
if (fbyte & 0x80) {
len -= 4;
p += 4;
au0828_isocdbg("Video frame %s\n",
(fbyte & 0x40) ? "odd" : "even"); if (fbyte & 0x40) { /* VBI */ if (vbi_buf != NULL)
buffer_filled(dev, vbi_dma_q, vbi_buf);
vbi_get_next_buf(vbi_dma_q, &vbi_buf); if (vbi_buf == NULL)
vbioutp = NULL; else
vbioutp = vb2_plane_vaddr(
&vbi_buf->vb.vb2_buf, 0);
/* Video */ if (buf != NULL)
buffer_filled(dev, dma_q, buf);
get_next_buf(dma_q, &buf); if (buf == NULL)
outp = NULL; else
outp = vb2_plane_vaddr(
&buf->vb.vb2_buf, 0);
/* As long as isoc traffic is arriving, keep
resetting the timer */ if (dev->vid_timeout_running)
mod_timer(&dev->vid_timeout,
jiffies + (HZ / 10)); if (dev->vbi_timeout_running)
mod_timer(&dev->vbi_timeout,
jiffies + (HZ / 10));
}
if (buf != NULL) { if (fbyte & 0x40)
buf->top_field = 1; else
buf->top_field = 0;
}
if (vbi_buf != NULL) { if (fbyte & 0x40)
vbi_buf->top_field = 1; else
vbi_buf->top_field = 0;
}
void au0828_usb_v4l2_media_release(struct au0828_dev *dev)
{ #ifdef CONFIG_MEDIA_CONTROLLER int i;
for (i = 0; i < AU0828_MAX_INPUT; i++) { if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED) return;
media_device_unregister_entity(&dev->input_ent[i]);
} #endif
}
/* This control handler will inherit the controls from au8522 */
retval = v4l2_ctrl_handler_init(&dev->v4l2_ctrl_hdl, 4); if (retval) {
pr_err("%s() v4l2_ctrl_handler_init failed\n",
__func__); return retval;
}
dev->v4l2_dev.ctrl_handler = &dev->v4l2_ctrl_hdl;
if (vb2_plane_size(vb, 0) < buf->length) {
pr_err("%s data will not fit into plane (%lu < %lu)\n",
__func__, vb2_plane_size(vb, 0), buf->length); return -EINVAL;
}
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->length); return 0;
}
if (test_bit(DEV_DISCONNECTED, &d->dev_state)) return -ENODEV;
iface = usb_ifnum_to_if(d->usbdev, 0); if (iface && iface->cur_altsetting->desc.bAlternateSetting != 5) {
dprintk(1, "Changing intf#0 to alt 5\n"); /* set au0828 interface0 to AS5 here again */
ret = usb_set_interface(d->usbdev, 0, 5); if (ret < 0) {
pr_info("Au0828 can't set alt setting to 5!\n"); return -EBUSY;
}
}
h = d->height / 2 + 2;
w = d->width * 2;
au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00);
au0828_writereg(d, 0x106, 0x00); /* set x position */
au0828_writereg(d, 0x110, 0x00);
au0828_writereg(d, 0x111, 0x00);
au0828_writereg(d, 0x114, w & 0xff);
au0828_writereg(d, 0x115, w >> 8); /* set y position */
au0828_writereg(d, 0x112, 0x00);
au0828_writereg(d, 0x113, 0x00);
au0828_writereg(d, 0x116, h & 0xff);
au0828_writereg(d, 0x117, h >> 8);
au0828_writereg(d, AU0828_SENSORCTRL_100, 0xb3);
if (dev->streaming_users == 0) { /* If we were doing ac97 instead of i2s, it would go here...*/
au0828_i2s_init(dev);
rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB,
AU0828_MAX_ISO_BUFS, dev->max_pkt_size,
au0828_isoc_copy); if (rc < 0) {
pr_info("au0828_init_isoc failed\n"); return rc;
}
/* This function ensures that video frames continue to be delivered even if the ITU-656 input isn't receiving any data (thereby preventing applications
such as tvtime from hanging) */ staticvoid au0828_vid_buffer_timeout(struct timer_list *t)
{ struct au0828_dev *dev = timer_container_of(dev, t, vid_timeout); struct au0828_dmaqueue *dma_q = &dev->vidq; struct au0828_buffer *buf; unsignedchar *vid_data; unsignedlong flags = 0;
mutex_lock(&dev->lock); if (vdev->vfl_type == VFL_TYPE_VIDEO && dev->vid_timeout_running) { /* Cancel timeout thread in case they didn't call streamoff */
dev->vid_timeout_running = 0;
timer_delete_sync(&dev->vid_timeout);
} elseif (vdev->vfl_type == VFL_TYPE_VBI &&
dev->vbi_timeout_running) { /* Cancel timeout thread in case they didn't call streamoff */
dev->vbi_timeout_running = 0;
timer_delete_sync(&dev->vbi_timeout);
}
if (test_bit(DEV_DISCONNECTED, &dev->dev_state)) goto end;
if (dev->users == 1) { /* * Avoid putting tuner in sleep if DVB or ALSA are * streaming. * * On most USB devices like au0828 the tuner can * be safely put in sleep state here if ALSA isn't * streaming. Exceptions are some very old USB tuner * models such as em28xx-based WinTV USB2 which have * a separate audio output jack. The devices that have * a separate audio output jack have analog tuners, * like Philips FM1236. Those devices are always on, * so the s_power callback are silently ignored. * So, the current logic here does the following: * Disable (put tuner to sleep) when * - ALSA and DVB aren't streaming. * - the last V4L2 file handler is closed. * * FIXME: * * Additionally, this logic could be improved to * disable the media source if the above conditions * are met and if the device: * - doesn't have a separate audio out plug (or * - doesn't use a silicon tuner like xc2028/3028/4000/5000). * * Once this additional logic is in place, a callback * is needed to enable the media source and power on * the tuner, for radio to work.
*/
ret = v4l_enable_media_source(vdev); if (ret == 0)
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner,
standby);
dev->std_set_in_tuner_core = 0;
/* When close the device, set the usb intf0 into alt0 to free
USB bandwidth */
ret = usb_set_interface(dev->usbdev, 0, 0); if (ret < 0)
pr_info("Au0828 can't set alternate to 0!\n");
}
end:
_vb2_fop_release(filp, NULL);
dev->users--;
mutex_unlock(&dev->lock); return 0;
}
/* Must be called with dev->lock held */ staticvoid au0828_init_tuner(struct au0828_dev *dev)
{ struct v4l2_frequency f = {
.frequency = dev->ctrl_freq,
.type = V4L2_TUNER_ANALOG_TV,
};
dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
if (dev->std_set_in_tuner_core) return;
dev->std_set_in_tuner_core = 1;
i2c_gate_ctrl(dev, 1); /* If we've never sent the standard in tuner core, do so now. We don't do this at device probe because we don't want to
incur the cost of a firmware load */
v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->std);
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
i2c_gate_ctrl(dev, 0);
}
staticint au0828_set_format(struct au0828_dev *dev, unsignedint cmd, struct v4l2_format *format)
{ int ret; int width = format->fmt.pix.width; int height = format->fmt.pix.height;
/* If they are demanding a format other than the one we support,
bail out (tvtime asks for UYVY and then retries with YUYV) */ if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY) return -EINVAL;
/* format->fmt.pix.width only support 720 and height 480 */ if (width != 720)
width = 720; if (height != 480)
height = 480;
dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
if (norm == dev->std) return 0;
if (dev->streaming_users > 0) return -EBUSY;
dev->std = norm;
au0828_init_tuner(dev);
i2c_gate_ctrl(dev, 1);
/* * FIXME: when we support something other than 60Hz standards, * we are going to have to make the au0828 bridge adjust the size * of its capture buffer, which is currently hardcoded at 720x480
*/
for (i = 0; i < AU0828_MAX_INPUT; i++) { int enable = 0; if (AUVI_INPUT(i).audio_setup == NULL) continue;
if (i == index)
enable = 1; else
enable = 0; if (enable) {
(AUVI_INPUT(i).audio_setup)(dev, enable);
} else { /* Make sure we leave it turned on if some
other input is routed to this callback */ if ((AUVI_INPUT(i).audio_setup) !=
((AUVI_INPUT(index).audio_setup))) {
(AUVI_INPUT(i).audio_setup)(dev, enable);
}
}
}
dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__,
index); if (index >= AU0828_MAX_INPUT) return -EINVAL; if (AUVI_INPUT(index).type == 0) return -EINVAL;
if (dev->ctrl_input == index) return 0;
au0828_s_input(dev, index);
/* * Input has been changed. Disable the media source * associated with the old input and enable source * for the newly set input
*/
v4l_disable_media_source(vfd); return v4l_enable_media_source(vfd);
}
dprintk(1, "%s called std_set %d dev_state %ld\n", __func__,
dev->std_set_in_tuner_core, dev->dev_state);
au0828_init_tuner(dev);
i2c_gate_ctrl(dev, 1);
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq); /* Get the actual set (and possibly clamped) frequency */
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, &new_freq);
dev->ctrl_freq = new_freq.frequency;
void au0828_v4l2_suspend(struct au0828_dev *dev)
{ struct urb *urb; int i;
pr_info("stopping V4L2\n");
if (dev->stream_state == STREAM_ON) {
pr_info("stopping V4L2 active URBs\n");
au0828_analog_stream_disable(dev); /* stop urbs */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
urb = dev->isoc_ctl.urb[i]; if (urb) { if (!irqs_disabled())
usb_kill_urb(urb); else
usb_unlink_urb(urb);
}
}
}
if (dev->vid_timeout_running)
timer_delete_sync(&dev->vid_timeout); if (dev->vbi_timeout_running)
timer_delete_sync(&dev->vbi_timeout);
}
void au0828_v4l2_resume(struct au0828_dev *dev)
{ int i, rc;
pr_info("restarting V4L2\n");
if (dev->stream_state == STREAM_ON) {
au0828_stream_interrupt(dev);
au0828_init_tuner(dev);
}
if (dev->vid_timeout_running)
mod_timer(&dev->vid_timeout, jiffies + (HZ / 10)); if (dev->vbi_timeout_running)
mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
/* If we were doing ac97 instead of i2s, it would go here...*/
au0828_i2s_init(dev);
au0828_analog_stream_enable(dev);
if (!(dev->stream_state == STREAM_ON)) {
au0828_analog_stream_reset(dev); /* submit urbs */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); if (rc) {
au0828_isocdbg("submit of urb %i failed (error=%i)\n",
i, rc);
au0828_uninit_isoc(dev);
}
}
}
}
/* Initialize Video and VBI pads */
dev->video_pad.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_pads_init(&dev->vdev.entity, 1, &dev->video_pad); if (ret < 0)
pr_err("failed to initialize video media entity!\n");
dev->vbi_pad.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_pads_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad); if (ret < 0)
pr_err("failed to initialize vbi media entity!\n");
/* Create entities for each input connector */ for (i = 0; i < AU0828_MAX_INPUT; i++) { struct media_entity *ent = &dev->input_ent[i];
if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED) break;
switch (AUVI_INPUT(i).type) { case AU0828_VMUX_COMPOSITE:
ent->function = MEDIA_ENT_F_CONN_COMPOSITE; break; case AU0828_VMUX_SVIDEO:
ent->function = MEDIA_ENT_F_CONN_SVIDEO; break; case AU0828_VMUX_CABLE: case AU0828_VMUX_TELEVISION: case AU0828_VMUX_DVB: default: /* Just to shut up a warning */
ent->function = MEDIA_ENT_F_CONN_RF; break;
}
ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]); if (ret < 0)
pr_err("failed to initialize input pad[%d]!\n", i);
ret = media_device_register_entity(dev->media_dev, ent); if (ret < 0)
pr_err("failed to register input entity %d!\n", i);
} #endif
}
int au0828_analog_register(struct au0828_dev *dev, struct usb_interface *interface)
{ int retval = -ENOMEM; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; int i, ret;
dprintk(1, "au0828_analog_register called for intf#%d!\n",
interface->cur_altsetting->desc.bInterfaceNumber);
/* No analog TV */ if (AUVI_INPUT(0).type == AU0828_VMUX_UNDEFINED) return 0;
/* set au0828 usb interface0 to as5 */
retval = usb_set_interface(dev->usbdev,
interface->cur_altsetting->desc.bInterfaceNumber, 5); if (retval != 0) {
pr_info("Failure setting usb interface0 to as5\n"); return retval;
}
/* Figure out which endpoint has the isoc interface */
iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
endpoint = &iface_desc->endpoint[i].desc; if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
== USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_ISOC)) {
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.