diff options
Diffstat (limited to 'drivers/media/common')
20 files changed, 2494 insertions, 304 deletions
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 71f8e018e564..7d42c11c8684 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -198,7 +198,6 @@ static int fops_open(struct file *file) struct saa7146_dev *dev = video_drvdata(file); struct saa7146_fh *fh = NULL; int result = 0; - enum v4l2_buf_type type; DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev)); @@ -227,11 +226,12 @@ static int fops_open(struct file *file) goto out; } - file->private_data = fh; + v4l2_fh_init(&fh->fh, vdev); + + file->private_data = &fh->fh; fh->dev = dev; - fh->type = type; - if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (vdev->vfl_type == VFL_TYPE_VBI) { DEB_S("initializing vbi...\n"); if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) result = saa7146_vbi_uops.open(dev,file); @@ -252,6 +252,7 @@ static int fops_open(struct file *file) } result = 0; + v4l2_fh_add(&fh->fh); out: if (fh && result != 0) { kfree(fh); @@ -263,6 +264,7 @@ out: static int fops_release(struct file *file) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; struct saa7146_dev *dev = fh->dev; @@ -271,7 +273,7 @@ static int fops_release(struct file *file) if (mutex_lock_interruptible(&saa7146_devices_lock)) return -ERESTARTSYS; - if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (vdev->vfl_type == VFL_TYPE_VBI) { if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) saa7146_vbi_uops.release(dev,file); if (dev->ext_vv_data->vbi_fops.release) @@ -280,6 +282,8 @@ static int fops_release(struct file *file) saa7146_video_uops.release(dev,file); } + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); module_put(dev->ext->module); file->private_data = NULL; kfree(fh); @@ -291,19 +295,22 @@ static int fops_release(struct file *file) static int fops_mmap(struct file *file, struct vm_area_struct * vma) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; struct videobuf_queue *q; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: { DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n", file, vma); q = &fh->video_q; break; } - case V4L2_BUF_TYPE_VBI_CAPTURE: { + case VFL_TYPE_VBI: { DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n", file, vma); + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) + return -ENODEV; q = &fh->vbi_q; break; } @@ -317,15 +324,19 @@ static int fops_mmap(struct file *file, struct vm_area_struct * vma) static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; struct videobuf_buffer *buf = NULL; struct videobuf_queue *q; + unsigned int res = v4l2_ctrl_poll(file, wait); DEB_EE("file:%p, poll:%p\n", file, wait); - if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { + if (vdev->vfl_type == VFL_TYPE_VBI) { + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) + return res | POLLOUT | POLLWRNORM; if( 0 == fh->vbi_q.streaming ) - return videobuf_poll_stream(file, &fh->vbi_q, wait); + return res | videobuf_poll_stream(file, &fh->vbi_q, wait); q = &fh->vbi_q; } else { DEB_D("using video queue\n"); @@ -337,31 +348,32 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) if (!buf) { DEB_D("buf == NULL!\n"); - return POLLERR; + return res | POLLERR; } poll_wait(file, &buf->done, wait); if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) { DEB_D("poll succeeded!\n"); - return POLLIN|POLLRDNORM; + return res | POLLIN | POLLRDNORM; } DEB_D("nothing to poll for, buf->state:%d\n", buf->state); - return 0; + return res; } static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: /* DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count); */ return saa7146_video_uops.read(file,data,count,ppos); - case V4L2_BUF_TYPE_VBI_CAPTURE: + case VFL_TYPE_VBI: /* DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count); @@ -377,12 +389,13 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: return -EINVAL; - case V4L2_BUF_TYPE_VBI_CAPTURE: + case VFL_TYPE_VBI: if (fh->dev->ext_vv_data->vbi_fops.write) return fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos); else @@ -429,8 +442,15 @@ static void vv_callback(struct saa7146_dev *dev, unsigned long status) } } +static const struct v4l2_ctrl_ops saa7146_ctrl_ops = { + .s_ctrl = saa7146_s_ctrl, +}; + int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) { + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + struct v4l2_pix_format *fmt; + struct v4l2_vbi_format *vbi; struct saa7146_vv *vv; int err; @@ -438,12 +458,32 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) if (err) return err; + v4l2_ctrl_handler_init(hdl, 6); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (hdl->error) { + err = hdl->error; + v4l2_ctrl_handler_free(hdl); + return err; + } + dev->v4l2_dev.ctrl_handler = hdl; + vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL); if (vv == NULL) { ERR("out of memory. aborting.\n"); + v4l2_ctrl_handler_free(hdl); return -ENOMEM; } - ext_vv->ops = saa7146_video_ioctl_ops; + ext_vv->vid_ops = saa7146_video_ioctl_ops; + ext_vv->vbi_ops = saa7146_vbi_ioctl_ops; ext_vv->core_ops = &saa7146_video_ioctl_ops; DEB_EE("dev:%p\n", dev); @@ -463,6 +503,7 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) if( NULL == vv->d_clipping.cpu_addr ) { ERR("out of memory. aborting.\n"); kfree(vv); + v4l2_ctrl_handler_free(hdl); return -1; } memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM); @@ -471,6 +512,39 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) saa7146_vbi_uops.init(dev,vv); + fmt = &vv->ov_fb.fmt; + fmt->width = vv->standard->h_max_out; + fmt->height = vv->standard->v_max_out; + fmt->pixelformat = V4L2_PIX_FMT_RGB565; + fmt->bytesperline = 2 * fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + + fmt = &vv->video_fmt; + fmt->width = 384; + fmt->height = 288; + fmt->pixelformat = V4L2_PIX_FMT_BGR24; + fmt->field = V4L2_FIELD_ANY; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->bytesperline = 3 * fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height; + + vbi = &vv->vbi_fmt; + vbi->sampling_rate = 27000000; + vbi->offset = 248; /* todo */ + vbi->samples_per_line = 720 * 2; + vbi->sample_format = V4L2_PIX_FMT_GREY; + + /* fixme: this only works for PAL */ + vbi->start[0] = 5; + vbi->count[0] = 16; + vbi->start[1] = 312; + vbi->count[1] = 16; + + init_timer(&vv->vbi_read_timeout); + + vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING; + vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY; dev->vv_data = vv; dev->vv_callback = &vv_callback; @@ -486,6 +560,7 @@ int saa7146_vv_release(struct saa7146_dev* dev) v4l2_device_unregister(&dev->v4l2_dev); pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); + v4l2_ctrl_handler_free(&dev->ctrl_handler); kfree(vv); dev->vv_data = NULL; dev->vv_callback = NULL; @@ -509,10 +584,19 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, return -ENOMEM; vfd->fops = &video_fops; - vfd->ioctl_ops = &dev->ext_vv_data->ops; + if (type == VFL_TYPE_GRABBER) + vfd->ioctl_ops = &dev->ext_vv_data->vid_ops; + else + vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops; vfd->release = video_device_release; + /* Locking in file operations other than ioctl should be done by + the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &dev->v4l2_lock; + vfd->v4l2_dev = &dev->v4l2_dev; vfd->tvnorms = 0; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); for (i = 0; i < dev->ext_vv_data->num_stds; i++) vfd->tvnorms |= dev->ext_vv_data->stds[i].id; strlcpy(vfd->name, name, sizeof(vfd->name)); diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c index bc1f545c95cb..be746d1aee9a 100644 --- a/drivers/media/common/saa7146_hlp.c +++ b/drivers/media/common/saa7146_hlp.c @@ -343,9 +343,9 @@ static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct sa struct saa7146_vv *vv = dev->vv_data; __le32 *clipping = vv->d_clipping.cpu_addr; - int width = fh->ov.win.w.width; - int height = fh->ov.win.w.height; - int clipcount = fh->ov.nclips; + int width = vv->ov.win.w.width; + int height = vv->ov.win.w.height; + int clipcount = vv->ov.nclips; u32 line_list[32]; u32 pixel_list[32]; @@ -365,10 +365,10 @@ static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct sa for(i = 0; i < clipcount; i++) { int l = 0, r = 0, t = 0, b = 0; - x[i] = fh->ov.clips[i].c.left; - y[i] = fh->ov.clips[i].c.top; - w[i] = fh->ov.clips[i].c.width; - h[i] = fh->ov.clips[i].c.height; + x[i] = vv->ov.clips[i].c.left; + y[i] = vv->ov.clips[i].c.top; + w[i] = vv->ov.clips[i].c.width; + h[i] = vv->ov.clips[i].c.height; if( w[i] < 0) { x[i] += w[i]; w[i] = -w[i]; @@ -485,13 +485,14 @@ static void saa7146_disable_clipping(struct saa7146_dev *dev) static void saa7146_set_clipping_rect(struct saa7146_fh *fh) { struct saa7146_dev *dev = fh->dev; - enum v4l2_field field = fh->ov.win.field; + struct saa7146_vv *vv = dev->vv_data; + enum v4l2_field field = vv->ov.win.field; struct saa7146_video_dma vdma2; u32 clip_format; u32 arbtr_ctrl; /* check clipcount, disable clipping if clipcount == 0*/ - if( fh->ov.nclips == 0 ) { + if (vv->ov.nclips == 0) { saa7146_disable_clipping(dev); return; } @@ -651,8 +652,8 @@ int saa7146_enable_overlay(struct saa7146_fh *fh) struct saa7146_dev *dev = fh->dev; struct saa7146_vv *vv = dev->vv_data; - saa7146_set_window(dev, fh->ov.win.w.width, fh->ov.win.w.height, fh->ov.win.field); - saa7146_set_position(dev, fh->ov.win.w.left, fh->ov.win.w.top, fh->ov.win.w.height, fh->ov.win.field, vv->ov_fmt->pixelformat); + saa7146_set_window(dev, vv->ov.win.w.width, vv->ov.win.w.height, vv->ov.win.field); + saa7146_set_position(dev, vv->ov.win.w.left, vv->ov.win.w.top, vv->ov.win.w.height, vv->ov.win.field, vv->ov_fmt->pixelformat); saa7146_set_output_format(dev, vv->ov_fmt->trans); saa7146_set_clipping_rect(fh); diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c index b2e718343739..1e71e374bbfe 100644 --- a/drivers/media/common/saa7146_vbi.c +++ b/drivers/media/common/saa7146_vbi.c @@ -211,7 +211,7 @@ static int buffer_activate(struct saa7146_dev *dev, DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next); saa7146_set_vbi_capture(dev,buf,next); - mod_timer(&vv->vbi_q.timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT); return 0; } @@ -294,7 +294,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7146_buf *buf = (struct saa7146_buf *)vb; DEB_VBI("vb:%p\n", vb); - saa7146_buffer_queue(dev,&vv->vbi_q,buf); + saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf); } static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) @@ -335,16 +335,15 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file) /* shut down dma 3 transfers */ saa7146_write(dev, MC1, MASK_20); - if (vv->vbi_q.curr) { - saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE); - } + if (vv->vbi_dmaq.curr) + saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); videobuf_queue_cancel(&fh->vbi_q); vv->vbi_streaming = NULL; - del_timer(&vv->vbi_q.timeout); - del_timer(&fh->vbi_read_timeout); + del_timer(&vv->vbi_dmaq.timeout); + del_timer(&vv->vbi_read_timeout); spin_unlock_irqrestore(&dev->slock, flags); } @@ -364,12 +363,12 @@ static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) { DEB_VBI("dev:%p\n", dev); - INIT_LIST_HEAD(&vv->vbi_q.queue); + INIT_LIST_HEAD(&vv->vbi_dmaq.queue); - init_timer(&vv->vbi_q.timeout); - vv->vbi_q.timeout.function = saa7146_buffer_timeout; - vv->vbi_q.timeout.data = (unsigned long)(&vv->vbi_q); - vv->vbi_q.dev = dev; + init_timer(&vv->vbi_dmaq.timeout); + vv->vbi_dmaq.timeout.function = saa7146_buffer_timeout; + vv->vbi_dmaq.timeout.data = (unsigned long)(&vv->vbi_dmaq); + vv->vbi_dmaq.dev = dev; init_waitqueue_head(&vv->vbi_wq); } @@ -377,6 +376,7 @@ static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) static int vbi_open(struct saa7146_dev *dev, struct file *file) { struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = fh->dev->vv_data; u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); int ret = 0; @@ -395,19 +395,6 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file) saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); saa7146_write(dev, MC2, (MASK_04|MASK_20)); - memset(&fh->vbi_fmt,0,sizeof(fh->vbi_fmt)); - - fh->vbi_fmt.sampling_rate = 27000000; - fh->vbi_fmt.offset = 248; /* todo */ - fh->vbi_fmt.samples_per_line = vbi_pixel_to_capture; - fh->vbi_fmt.sample_format = V4L2_PIX_FMT_GREY; - - /* fixme: this only works for PAL */ - fh->vbi_fmt.start[0] = 5; - fh->vbi_fmt.count[0] = 16; - fh->vbi_fmt.start[1] = 312; - fh->vbi_fmt.count[1] = 16; - videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops, &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VBI_CAPTURE, @@ -415,9 +402,8 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file) sizeof(struct saa7146_buf), file, &dev->v4l2_lock); - init_timer(&fh->vbi_read_timeout); - fh->vbi_read_timeout.function = vbi_read_timeout; - fh->vbi_read_timeout.data = (unsigned long)file; + vv->vbi_read_timeout.function = vbi_read_timeout; + vv->vbi_read_timeout.data = (unsigned long)file; /* initialize the brs */ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { @@ -453,16 +439,16 @@ static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) struct saa7146_vv *vv = dev->vv_data; spin_lock(&dev->slock); - if (vv->vbi_q.curr) { - DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_q.curr); + if (vv->vbi_dmaq.curr) { + DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr); /* this must be += 2, one count for each field */ vv->vbi_fieldcount+=2; - vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount; - saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE); + vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount; + saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); } else { DEB_VBI("dev:%p\n", dev); } - saa7146_buffer_next(dev,&vv->vbi_q,1); + saa7146_buffer_next(dev, &vv->vbi_dmaq, 1); spin_unlock(&dev->slock); } @@ -488,7 +474,7 @@ static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff return -EBUSY; } - mod_timer(&fh->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1, file->f_flags & O_NONBLOCK); /* diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index ce30533fd972..6d14785d4747 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -2,6 +2,8 @@ #include <media/saa7146_vv.h> #include <media/v4l2-chip-ident.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ctrls.h> #include <linux/module.h> static int max_memory = 32; @@ -112,8 +114,8 @@ int saa7146_start_preview(struct saa7146_fh *fh) DEB_EE("dev:%p, fh:%p\n", dev, fh); - /* check if we have overlay informations */ - if( NULL == fh->ov.fh ) { + /* check if we have overlay information */ + if (vv->ov.fh == NULL) { DEB_D("no overlay data available. try S_FMT first.\n"); return -EAGAIN; } @@ -139,19 +141,18 @@ int saa7146_start_preview(struct saa7146_fh *fh) return -EBUSY; } - fmt.fmt.win = fh->ov.win; + fmt.fmt.win = vv->ov.win; err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt); if (0 != err) { saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); return -EBUSY; } - fh->ov.win = fmt.fmt.win; - vv->ov_data = &fh->ov; + vv->ov.win = fmt.fmt.win; DEB_D("%dx%d+%d+%d %s field=%s\n", - fh->ov.win.w.width, fh->ov.win.w.height, - fh->ov.win.w.left, fh->ov.win.w.top, - vv->ov_fmt->name, v4l2_field_names[fh->ov.win.field]); + vv->ov.win.w.width, vv->ov.win.w.height, + vv->ov.win.w.left, vv->ov.win.w.top, + vv->ov_fmt->name, v4l2_field_names[vv->ov.win.field]); if (0 != (ret = saa7146_enable_overlay(fh))) { DEB_D("enabling overlay failed: %d\n", ret); @@ -202,65 +203,6 @@ int saa7146_stop_preview(struct saa7146_fh *fh) EXPORT_SYMBOL_GPL(saa7146_stop_preview); /********************************************************************************/ -/* device controls */ - -static struct v4l2_queryctrl controls[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - .type = V4L2_CTRL_TYPE_INTEGER, - .flags = V4L2_CTRL_FLAG_SLIDER, - },{ - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - .type = V4L2_CTRL_TYPE_INTEGER, - .flags = V4L2_CTRL_FLAG_SLIDER, - },{ - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - .type = V4L2_CTRL_TYPE_INTEGER, - .flags = V4L2_CTRL_FLAG_SLIDER, - },{ - .id = V4L2_CID_VFLIP, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_HFLIP, - .name = "Horizontal Flip", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, -}; -static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl); - -#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0) - -static struct v4l2_queryctrl* ctrl_by_id(int id) -{ - int i; - - for (i = 0; i < NUM_CONTROLS; i++) - if (controls[i].id == id) - return controls+i; - return NULL; -} - -/********************************************************************************/ /* common pagetable functions */ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf) @@ -413,7 +355,7 @@ static int video_begin(struct saa7146_fh *fh) } } - fmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat); + fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); /* we need to have a valid format set here */ BUG_ON(NULL == fmt); @@ -465,7 +407,7 @@ static int video_end(struct saa7146_fh *fh, struct file *file) return -EBUSY; } - fmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat); + fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); /* we need to have a valid format set here */ BUG_ON(NULL == fmt); @@ -504,18 +446,25 @@ static int video_end(struct saa7146_fh *fh, struct file *file) static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { + struct video_device *vdev = video_devdata(file); struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; strcpy((char *)cap->driver, "saa7146 v4l2"); strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci)); - cap->version = SAA7146_VERSION_CODE; - cap->capabilities = + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - cap->capabilities |= dev->ext_vv_data->capabilities; + cap->device_caps |= dev->ext_vv_data->capabilities; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + cap->device_caps &= + ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); + else + cap->device_caps &= + ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO); return 0; } @@ -526,6 +475,7 @@ static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f *fb = vv->ov_fb; fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + fb->flags = V4L2_FBUF_FLAG_PRIMARY; return 0; } @@ -579,135 +529,58 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtd return 0; } -static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c) +int saa7146_s_ctrl(struct v4l2_ctrl *ctrl) { - const struct v4l2_queryctrl *ctrl; - - if ((c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) && - (c->id < V4L2_CID_PRIVATE_BASE || - c->id >= V4L2_CID_PRIVATE_LASTP1)) - return -EINVAL; - - ctrl = ctrl_by_id(c->id); - if (ctrl == NULL) - return -EINVAL; - - DEB_EE("VIDIOC_QUERYCTRL: id:%d\n", c->id); - *c = *ctrl; - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_dev *dev = container_of(ctrl->handler, + struct saa7146_dev, ctrl_handler); struct saa7146_vv *vv = dev->vv_data; - const struct v4l2_queryctrl *ctrl; - u32 value = 0; + u32 val; - ctrl = ctrl_by_id(c->id); - if (NULL == ctrl) - return -EINVAL; - switch (c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - value = saa7146_read(dev, BCS_CTRL); - c->value = 0xff & (value >> 24); - DEB_D("V4L2_CID_BRIGHTNESS: %d\n", c->value); - break; - case V4L2_CID_CONTRAST: - value = saa7146_read(dev, BCS_CTRL); - c->value = 0x7f & (value >> 16); - DEB_D("V4L2_CID_CONTRAST: %d\n", c->value); - break; - case V4L2_CID_SATURATION: - value = saa7146_read(dev, BCS_CTRL); - c->value = 0x7f & (value >> 0); - DEB_D("V4L2_CID_SATURATION: %d\n", c->value); - break; - case V4L2_CID_VFLIP: - c->value = vv->vflip; - DEB_D("V4L2_CID_VFLIP: %d\n", c->value); - break; - case V4L2_CID_HFLIP: - c->value = vv->hflip; - DEB_D("V4L2_CID_HFLIP: %d\n", c->value); - break; - default: - return -EINVAL; - } - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - const struct v4l2_queryctrl *ctrl; - - ctrl = ctrl_by_id(c->id); - if (NULL == ctrl) { - DEB_D("unknown control %d\n", c->id); - return -EINVAL; - } - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_MENU: - case V4L2_CTRL_TYPE_INTEGER: - if (c->value < ctrl->minimum) - c->value = ctrl->minimum; - if (c->value > ctrl->maximum) - c->value = ctrl->maximum; - break; - default: - /* nothing */; - } - - switch (c->id) { - case V4L2_CID_BRIGHTNESS: { - u32 value = saa7146_read(dev, BCS_CTRL); - value &= 0x00ffffff; - value |= (c->value << 24); - saa7146_write(dev, BCS_CTRL, value); + val = saa7146_read(dev, BCS_CTRL); + val &= 0x00ffffff; + val |= (ctrl->val << 24); + saa7146_write(dev, BCS_CTRL, val); saa7146_write(dev, MC2, MASK_22 | MASK_06); break; - } - case V4L2_CID_CONTRAST: { - u32 value = saa7146_read(dev, BCS_CTRL); - value &= 0xff00ffff; - value |= (c->value << 16); - saa7146_write(dev, BCS_CTRL, value); + + case V4L2_CID_CONTRAST: + val = saa7146_read(dev, BCS_CTRL); + val &= 0xff00ffff; + val |= (ctrl->val << 16); + saa7146_write(dev, BCS_CTRL, val); saa7146_write(dev, MC2, MASK_22 | MASK_06); break; - } - case V4L2_CID_SATURATION: { - u32 value = saa7146_read(dev, BCS_CTRL); - value &= 0xffffff00; - value |= (c->value << 0); - saa7146_write(dev, BCS_CTRL, value); + + case V4L2_CID_SATURATION: + val = saa7146_read(dev, BCS_CTRL); + val &= 0xffffff00; + val |= (ctrl->val << 0); + saa7146_write(dev, BCS_CTRL, val); saa7146_write(dev, MC2, MASK_22 | MASK_06); break; - } + case V4L2_CID_HFLIP: /* fixme: we can support changing VFLIP and HFLIP here... */ - if (IS_CAPTURE_ACTIVE(fh) != 0) { - DEB_D("V4L2_CID_HFLIP while active capture\n"); + if ((vv->video_status & STATUS_CAPTURE)) return -EBUSY; - } - vv->hflip = c->value; + vv->hflip = ctrl->val; break; + case V4L2_CID_VFLIP: - if (IS_CAPTURE_ACTIVE(fh) != 0) { - DEB_D("V4L2_CID_VFLIP while active capture\n"); + if ((vv->video_status & STATUS_CAPTURE)) return -EBUSY; - } - vv->vflip = c->value; + vv->vflip = ctrl->val; break; + default: return -EINVAL; } - if (IS_OVERLAY_ACTIVE(fh) != 0) { + if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */ + struct saa7146_fh *fh = vv->video_fh; + saa7146_stop_preview(fh); saa7146_start_preview(fh); } @@ -720,6 +593,8 @@ static int vidioc_g_parm(struct file *file, void *fh, struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct saa7146_vv *vv = dev->vv_data; + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; parm->parm.capture.readbuffers = 1; v4l2_video_std_frame_period(vv->standard->id, &parm->parm.capture.timeperframe); @@ -728,19 +603,28 @@ static int vidioc_g_parm(struct file *file, void *fh, static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { - f->fmt.pix = ((struct saa7146_fh *)fh)->video_fmt; + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.pix = vv->video_fmt; return 0; } static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) { - f->fmt.win = ((struct saa7146_fh *)fh)->ov.win; + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.win = vv->ov.win; return 0; } static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) { - f->fmt.vbi = ((struct saa7146_fh *)fh)->vbi_fmt; + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.vbi = vv->vbi_fmt; return 0; } @@ -787,6 +671,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma } f->fmt.pix.field = field; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; if (f->fmt.pix.width > maxw) f->fmt.pix.width = maxw; if (f->fmt.pix.height > maxh) @@ -883,9 +768,9 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_forma err = vidioc_try_fmt_vid_cap(file, fh, f); if (0 != err) return err; - fh->video_fmt = f->fmt.pix; + vv->video_fmt = f->fmt.pix; DEB_EE("set to pixelformat '%4.4s'\n", - (char *)&fh->video_fmt.pixelformat); + (char *)&vv->video_fmt.pixelformat); return 0; } @@ -900,17 +785,17 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_f err = vidioc_try_fmt_vid_overlay(file, fh, f); if (0 != err) return err; - fh->ov.win = f->fmt.win; - fh->ov.nclips = f->fmt.win.clipcount; - if (fh->ov.nclips > 16) - fh->ov.nclips = 16; - if (copy_from_user(fh->ov.clips, f->fmt.win.clips, - sizeof(struct v4l2_clip) * fh->ov.nclips)) { + vv->ov.win = f->fmt.win; + vv->ov.nclips = f->fmt.win.clipcount; + if (vv->ov.nclips > 16) + vv->ov.nclips = 16; + if (copy_from_user(vv->ov.clips, f->fmt.win.clips, + sizeof(struct v4l2_clip) * vv->ov.nclips)) { return -EFAULT; } - /* fh->ov.fh is used to indicate that we have valid overlay informations, too */ - fh->ov.fh = fh; + /* vv->ov.fh is used to indicate that we have valid overlay informations, too */ + vv->ov.fh = fh; /* check if our current overlay is active */ if (IS_OVERLAY_ACTIVE(fh) != 0) { @@ -1111,10 +996,14 @@ static int vidioc_g_chip_ident(struct file *file, void *__fh, chip->ident = V4L2_IDENT_NONE; chip->revision = 0; - if (chip->match.type == V4L2_CHIP_MATCH_HOST && !chip->match.addr) { - chip->ident = V4L2_IDENT_SAA7146; + if (chip->match.type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(&chip->match)) + chip->ident = V4L2_IDENT_SAA7146; return 0; } + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; return v4l2_device_call_until_err(&dev->v4l2_dev, 0, core, g_chip_ident, chip); } @@ -1129,7 +1018,6 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, - .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_g_chip_ident = vidioc_g_chip_ident, .vidioc_overlay = vidioc_overlay, @@ -1141,12 +1029,29 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { .vidioc_dqbuf = vidioc_dqbuf, .vidioc_g_std = vidioc_g_std, .vidioc_s_std = vidioc_s_std, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_parm = vidioc_g_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_g_chip_ident = vidioc_g_chip_ident, + + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /*********************************************************************************/ @@ -1161,7 +1066,7 @@ static int buffer_activate (struct saa7146_dev *dev, buf->vb.state = VIDEOBUF_ACTIVE; saa7146_set_capture(dev,buf,next); - mod_timer(&vv->video_q.timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT); return 0; } @@ -1185,44 +1090,44 @@ static int buffer_prepare(struct videobuf_queue *q, DEB_CAP("vbuf:%p\n", vb); /* sanity checks */ - if (fh->video_fmt.width < 48 || - fh->video_fmt.height < 32 || - fh->video_fmt.width > vv->standard->h_max_out || - fh->video_fmt.height > vv->standard->v_max_out) { + if (vv->video_fmt.width < 48 || + vv->video_fmt.height < 32 || + vv->video_fmt.width > vv->standard->h_max_out || + vv->video_fmt.height > vv->standard->v_max_out) { DEB_D("w (%d) / h (%d) out of bounds\n", - fh->video_fmt.width, fh->video_fmt.height); + vv->video_fmt.width, vv->video_fmt.height); return -EINVAL; } - size = fh->video_fmt.sizeimage; + size = vv->video_fmt.sizeimage; if (0 != buf->vb.baddr && buf->vb.bsize < size) { DEB_D("size mismatch\n"); return -EINVAL; } DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n", - fh->video_fmt.width, fh->video_fmt.height, - size, v4l2_field_names[fh->video_fmt.field]); - if (buf->vb.width != fh->video_fmt.width || - buf->vb.bytesperline != fh->video_fmt.bytesperline || - buf->vb.height != fh->video_fmt.height || + vv->video_fmt.width, vv->video_fmt.height, + size, v4l2_field_names[vv->video_fmt.field]); + if (buf->vb.width != vv->video_fmt.width || + buf->vb.bytesperline != vv->video_fmt.bytesperline || + buf->vb.height != vv->video_fmt.height || buf->vb.size != size || buf->vb.field != field || - buf->vb.field != fh->video_fmt.field || - buf->fmt != &fh->video_fmt) { + buf->vb.field != vv->video_fmt.field || + buf->fmt != &vv->video_fmt) { saa7146_dma_free(dev,q,buf); } if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { struct saa7146_format *sfmt; - buf->vb.bytesperline = fh->video_fmt.bytesperline; - buf->vb.width = fh->video_fmt.width; - buf->vb.height = fh->video_fmt.height; + buf->vb.bytesperline = vv->video_fmt.bytesperline; + buf->vb.width = vv->video_fmt.width; + buf->vb.height = vv->video_fmt.height; buf->vb.size = size; buf->vb.field = field; - buf->fmt = &fh->video_fmt; - buf->vb.field = fh->video_fmt.field; + buf->fmt = &vv->video_fmt; + buf->vb.field = vv->video_fmt.field; sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); @@ -1258,11 +1163,12 @@ static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned { struct file *file = q->priv_data; struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = fh->dev->vv_data; if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS) *count = MAX_SAA7146_CAPTURE_BUFFERS; - *size = fh->video_fmt.sizeimage; + *size = vv->video_fmt.sizeimage; /* check if we exceed the "max_memory" parameter */ if( (*count * *size) > (max_memory*1048576) ) { @@ -1283,7 +1189,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7146_buf *buf = (struct saa7146_buf *)vb; DEB_CAP("vbuf:%p\n", vb); - saa7146_buffer_queue(fh->dev,&vv->video_q,buf); + saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf); } static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) @@ -1312,12 +1218,12 @@ static struct videobuf_queue_ops video_qops = { static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) { - INIT_LIST_HEAD(&vv->video_q.queue); + INIT_LIST_HEAD(&vv->video_dmaq.queue); - init_timer(&vv->video_q.timeout); - vv->video_q.timeout.function = saa7146_buffer_timeout; - vv->video_q.timeout.data = (unsigned long)(&vv->video_q); - vv->video_q.dev = dev; + init_timer(&vv->video_dmaq.timeout); + vv->video_dmaq.timeout.function = saa7146_buffer_timeout; + vv->video_dmaq.timeout.data = (unsigned long)(&vv->video_dmaq); + vv->video_dmaq.dev = dev; /* set some default values */ vv->standard = &dev->ext_vv_data->stds[0]; @@ -1331,15 +1237,6 @@ static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) static int video_open(struct saa7146_dev *dev, struct file *file) { struct saa7146_fh *fh = file->private_data; - struct saa7146_format *sfmt; - - fh->video_fmt.width = 384; - fh->video_fmt.height = 288; - fh->video_fmt.pixelformat = V4L2_PIX_FMT_BGR24; - fh->video_fmt.bytesperline = 0; - fh->video_fmt.field = V4L2_FIELD_ANY; - sfmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat); - fh->video_fmt.sizeimage = (fh->video_fmt.width * fh->video_fmt.height * sfmt->depth)/8; videobuf_queue_sg_init(&fh->video_q, &video_qops, &dev->pci->dev, &dev->slock, @@ -1371,7 +1268,7 @@ static void video_close(struct saa7146_dev *dev, struct file *file) static void video_irq_done(struct saa7146_dev *dev, unsigned long st) { struct saa7146_vv *vv = dev->vv_data; - struct saa7146_dmaqueue *q = &vv->video_q; + struct saa7146_dmaqueue *q = &vv->video_dmaq; spin_lock(&dev->slock); DEB_CAP("called\n"); diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 4a6d5cef3964..bbf4945149a9 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -204,6 +204,27 @@ config MEDIA_TUNER_TDA18218 help NXP TDA18218 silicon tuner driver. +config MEDIA_TUNER_FC0011 + tristate "Fitipower FC0011 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0011 silicon tuner driver. + +config MEDIA_TUNER_FC0012 + tristate "Fitipower FC0012 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0012 silicon tuner driver. + +config MEDIA_TUNER_FC0013 + tristate "Fitipower FC0013 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0013 silicon tuner driver. + config MEDIA_TUNER_TDA18212 tristate "NXP TDA18212 silicon tuner" depends on VIDEO_MEDIA && I2C @@ -211,4 +232,10 @@ config MEDIA_TUNER_TDA18212 help NXP TDA18212 silicon tuner driver. +config MEDIA_TUNER_TUA9001 + tristate "Infineon TUA 9001 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Infineon TUA 9001 silicon tuner driver. endmenu diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index f80407eb8998..891b80e60808 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -28,6 +28,10 @@ obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o +obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o +obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o +obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o +obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/fc0011.c b/drivers/media/common/tuners/fc0011.c new file mode 100644 index 000000000000..e4882546c283 --- /dev/null +++ b/drivers/media/common/tuners/fc0011.c @@ -0,0 +1,524 @@ +/* + * Fitipower FC0011 tuner driver + * + * Copyright (C) 2012 Michael Buesch <m@bues.ch> + * + * Derived from FC0012 tuner driver: + * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "fc0011.h" + + +/* Tuner registers */ +enum { + FC11_REG_0, + FC11_REG_FA, /* FA */ + FC11_REG_FP, /* FP */ + FC11_REG_XINHI, /* XIN high 8 bit */ + FC11_REG_XINLO, /* XIN low 8 bit */ + FC11_REG_VCO, /* VCO */ + FC11_REG_VCOSEL, /* VCO select */ + FC11_REG_7, /* Unknown tuner reg 7 */ + FC11_REG_8, /* Unknown tuner reg 8 */ + FC11_REG_9, + FC11_REG_10, /* Unknown tuner reg 10 */ + FC11_REG_11, /* Unknown tuner reg 11 */ + FC11_REG_12, + FC11_REG_RCCAL, /* RC calibrate */ + FC11_REG_VCOCAL, /* VCO calibrate */ + FC11_REG_15, + FC11_REG_16, /* Unknown tuner reg 16 */ + FC11_REG_17, + + FC11_NR_REGS, /* Number of registers */ +}; + +enum FC11_REG_VCOSEL_bits { + FC11_VCOSEL_2 = 0x08, /* VCO select 2 */ + FC11_VCOSEL_1 = 0x10, /* VCO select 1 */ + FC11_VCOSEL_CLKOUT = 0x20, /* Fix clock out */ + FC11_VCOSEL_BW7M = 0x40, /* 7MHz bw */ + FC11_VCOSEL_BW6M = 0x80, /* 6MHz bw */ +}; + +enum FC11_REG_RCCAL_bits { + FC11_RCCAL_FORCE = 0x10, /* force */ +}; + +enum FC11_REG_VCOCAL_bits { + FC11_VCOCAL_RUN = 0, /* VCO calibration run */ + FC11_VCOCAL_VALUEMASK = 0x3F, /* VCO calibration value mask */ + FC11_VCOCAL_OK = 0x40, /* VCO calibration Ok */ + FC11_VCOCAL_RESET = 0x80, /* VCO calibration reset */ +}; + + +struct fc0011_priv { + struct i2c_adapter *i2c; + u8 addr; + + u32 frequency; + u32 bandwidth; +}; + + +static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + struct i2c_msg msg = { .addr = priv->addr, + .flags = 0, .buf = buf, .len = 2 }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + dev_err(&priv->i2c->dev, + "I2C write reg failed, reg: %02x, val: %02x\n", + reg, val); + return -EIO; + } + + return 0; +} + +static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val) +{ + u8 dummy; + struct i2c_msg msg[2] = { + { .addr = priv->addr, + .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, + .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + dev_err(&priv->i2c->dev, + "I2C read failed, reg: %02x\n", reg); + return -EIO; + } + + return 0; +} + +static int fc0011_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static int fc0011_init(struct dvb_frontend *fe) +{ + struct fc0011_priv *priv = fe->tuner_priv; + int err; + + if (WARN_ON(!fe->callback)) + return -EINVAL; + + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_POWER, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Power-on callback failed\n"); + return err; + } + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_RESET, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Reset callback failed\n"); + return err; + } + + return 0; +} + +/* Initiate VCO calibration */ +static int fc0011_vcocal_trigger(struct fc0011_priv *priv) +{ + int err; + + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET); + if (err) + return err; + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); + if (err) + return err; + + return 0; +} + +/* Read VCO calibration value */ +static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value) +{ + int err; + + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); + if (err) + return err; + usleep_range(10000, 20000); + err = fc0011_readreg(priv, FC11_REG_VCOCAL, value); + if (err) + return err; + + return 0; +} + +static int fc0011_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct fc0011_priv *priv = fe->tuner_priv; + int err; + unsigned int i, vco_retries; + u32 freq = p->frequency / 1000; + u32 bandwidth = p->bandwidth_hz / 1000; + u32 fvco, xin, xdiv, xdivr; + u16 frac; + u8 fa, fp, vco_sel, vco_cal; + u8 regs[FC11_NR_REGS] = { }; + + regs[FC11_REG_7] = 0x0F; + regs[FC11_REG_8] = 0x3E; + regs[FC11_REG_10] = 0xB8; + regs[FC11_REG_11] = 0x80; + regs[FC11_REG_RCCAL] = 0x04; + err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); + err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); + err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); + err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); + err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return -EIO; + + /* Set VCO freq and VCO div */ + if (freq < 54000) { + fvco = freq * 64; + regs[FC11_REG_VCO] = 0x82; + } else if (freq < 108000) { + fvco = freq * 32; + regs[FC11_REG_VCO] = 0x42; + } else if (freq < 216000) { + fvco = freq * 16; + regs[FC11_REG_VCO] = 0x22; + } else if (freq < 432000) { + fvco = freq * 8; + regs[FC11_REG_VCO] = 0x12; + } else { + fvco = freq * 4; + regs[FC11_REG_VCO] = 0x0A; + } + + /* Calc XIN. The PLL reference frequency is 18 MHz. */ + xdiv = fvco / 18000; + frac = fvco - xdiv * 18000; + frac = (frac << 15) / 18000; + if (frac >= 16384) + frac += 32786; + if (!frac) + xin = 0; + else if (frac < 511) + xin = 512; + else if (frac < 65026) + xin = frac; + else + xin = 65024; + regs[FC11_REG_XINHI] = xin >> 8; + regs[FC11_REG_XINLO] = xin; + + /* Calc FP and FA */ + xdivr = xdiv; + if (fvco - xdiv * 18000 >= 9000) + xdivr += 1; /* round */ + fp = xdivr / 8; + fa = xdivr - fp * 8; + if (fa < 2) { + fp -= 1; + fa += 8; + } + if (fp > 0x1F) { + fp &= 0x1F; + fa &= 0xF; + } + if (fa >= fp) { + dev_warn(&priv->i2c->dev, + "fa %02X >= fp %02X, but trying to continue\n", + (unsigned int)(u8)fa, (unsigned int)(u8)fp); + } + regs[FC11_REG_FA] = fa; + regs[FC11_REG_FP] = fp; + + /* Select bandwidth */ + switch (bandwidth) { + case 8000: + break; + case 7000: + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M; + break; + default: + dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. " + "Using 6000 kHz.\n", + bandwidth); + bandwidth = 6000; + /* fallthrough */ + case 6000: + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M; + break; + } + + /* Pre VCO select */ + if (fvco < 2320000) { + vco_sel = 0; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + } else if (fvco < 3080000) { + vco_sel = 1; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + } else { + vco_sel = 2; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + } + + /* Fix for low freqs */ + if (freq < 45000) { + regs[FC11_REG_FA] = 0x6; + regs[FC11_REG_FP] = 0x11; + } + + /* Clock out fix */ + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT; + + /* Write the cached registers */ + for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) { + err = fc0011_writereg(priv, i, regs[i]); + if (err) + return err; + } + + /* VCO calibration */ + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + err = fc0011_vcocal_read(priv, &vco_cal); + if (err) + return err; + vco_retries = 0; + while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) { + /* Reset the tuner and try again */ + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_RESET, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Failed to reset tuner\n"); + return err; + } + /* Reinit tuner config */ + err = 0; + for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) + err |= fc0011_writereg(priv, i, regs[i]); + err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); + err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); + err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); + err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); + err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return -EIO; + /* VCO calibration */ + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + err = fc0011_vcocal_read(priv, &vco_cal); + if (err) + return err; + vco_retries++; + } + if (!(vco_cal & FC11_VCOCAL_OK)) { + dev_err(&priv->i2c->dev, + "Failed to read VCO calibration value (got %02X)\n", + (unsigned int)vco_cal); + return -EIO; + } + vco_cal &= FC11_VCOCAL_VALUEMASK; + + switch (vco_sel) { + case 0: + if (vco_cal < 8) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } + break; + case 1: + if (vco_cal < 5) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else if (vco_cal <= 48) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } + break; + case 2: + if (vco_cal > 53) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } + break; + } + err = fc0011_vcocal_read(priv, NULL); + if (err) + return err; + usleep_range(10000, 50000); + + err = fc0011_readreg(priv, FC11_REG_RCCAL, ®s[FC11_REG_RCCAL]); + if (err) + return err; + regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE; + err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return err; + err = fc0011_writereg(priv, FC11_REG_16, 0xB); + if (err) + return err; + + dev_dbg(&priv->i2c->dev, "Tuned to " + "fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X " + "vcocal=%02X(%u) bw=%u\n", + (unsigned int)regs[FC11_REG_FA], + (unsigned int)regs[FC11_REG_FP], + (unsigned int)regs[FC11_REG_XINHI], + (unsigned int)regs[FC11_REG_XINLO], + (unsigned int)regs[FC11_REG_VCO], + (unsigned int)regs[FC11_REG_VCOSEL], + (unsigned int)vco_cal, vco_retries, + (unsigned int)bandwidth); + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + + return 0; +} + +static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0011_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency; + + return 0; +} + +static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 0; + + return 0; +} + +static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0011_priv *priv = fe->tuner_priv; + + *bandwidth = priv->bandwidth; + + return 0; +} + +static const struct dvb_tuner_ops fc0011_tuner_ops = { + .info = { + .name = "Fitipower FC0011", + + .frequency_min = 45000000, + .frequency_max = 1000000000, + }, + + .release = fc0011_release, + .init = fc0011_init, + + .set_params = fc0011_set_params, + + .get_frequency = fc0011_get_frequency, + .get_if_frequency = fc0011_get_if_frequency, + .get_bandwidth = fc0011_get_bandwidth, +}; + +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config) +{ + struct fc0011_priv *priv; + + priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL); + if (!priv) + return NULL; + + priv->i2c = i2c; + priv->addr = config->i2c_address; + + fe->tuner_priv = priv; + fe->ops.tuner_ops = fc0011_tuner_ops; + + dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n"); + + return fe; +} +EXPORT_SYMBOL(fc0011_attach); + +MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver"); +MODULE_AUTHOR("Michael Buesch <m@bues.ch>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/fc0011.h b/drivers/media/common/tuners/fc0011.h new file mode 100644 index 000000000000..0ee581f122d2 --- /dev/null +++ b/drivers/media/common/tuners/fc0011.h @@ -0,0 +1,41 @@ +#ifndef LINUX_FC0011_H_ +#define LINUX_FC0011_H_ + +#include "dvb_frontend.h" + + +/** struct fc0011_config - fc0011 hardware config + * + * @i2c_address: I2C bus address. + */ +struct fc0011_config { + u8 i2c_address; +}; + +/** enum fc0011_fe_callback_commands - Frontend callbacks + * + * @FC0011_FE_CALLBACK_POWER: Power on tuner hardware. + * @FC0011_FE_CALLBACK_RESET: Request a tuner reset. + */ +enum fc0011_fe_callback_commands { + FC0011_FE_CALLBACK_POWER, + FC0011_FE_CALLBACK_RESET, +}; + +#if defined(CONFIG_MEDIA_TUNER_FC0011) ||\ + defined(CONFIG_MEDIA_TUNER_FC0011_MODULE) +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config); +#else +static inline +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config) +{ + dev_err(&i2c->dev, "fc0011 driver disabled in Kconfig\n"); + return NULL; +} +#endif + +#endif /* LINUX_FC0011_H_ */ diff --git a/drivers/media/common/tuners/fc0012-priv.h b/drivers/media/common/tuners/fc0012-priv.h new file mode 100644 index 000000000000..4577c917e616 --- /dev/null +++ b/drivers/media/common/tuners/fc0012-priv.h @@ -0,0 +1,43 @@ +/* + * Fitipower FC0012 tuner driver - private includes + * + * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _FC0012_PRIV_H_ +#define _FC0012_PRIV_H_ + +#define LOG_PREFIX "fc0012" + +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + +struct fc0012_priv { + struct i2c_adapter *i2c; + u8 addr; + u8 dual_master; + u8 xtal_freq; + + u32 frequency; + u32 bandwidth; +}; + +#endif diff --git a/drivers/media/common/tuners/fc0012.c b/drivers/media/common/tuners/fc0012.c new file mode 100644 index 000000000000..308135abd54c --- /dev/null +++ b/drivers/media/common/tuners/fc0012.c @@ -0,0 +1,467 @@ +/* + * Fitipower FC0012 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "fc0012.h" +#include "fc0012-priv.h" + +static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = {reg, val}; + struct i2c_msg msg = { + .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 + }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + err("I2C write reg failed, reg: %02x, val: %02x", reg, val); + return -EREMOTEIO; + } + return 0; +} + +static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + err("I2C read reg failed, reg: %02x", reg); + return -EREMOTEIO; + } + return 0; +} + +static int fc0012_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int fc0012_init(struct dvb_frontend *fe) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int i, ret = 0; + unsigned char reg[] = { + 0x00, /* dummy reg. 0 */ + 0x05, /* reg. 0x01 */ + 0x10, /* reg. 0x02 */ + 0x00, /* reg. 0x03 */ + 0x00, /* reg. 0x04 */ + 0x0f, /* reg. 0x05: may also be 0x0a */ + 0x00, /* reg. 0x06: divider 2, VCO slow */ + 0x00, /* reg. 0x07: may also be 0x0f */ + 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, + Loop Bw 1/8 */ + 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */ + 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ + 0x82, /* reg. 0x0b: Output Clock is same as clock frequency, + may also be 0x83 */ + 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ + 0x02, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, 0x02 for DVB-T */ + 0x00, /* reg. 0x0e */ + 0x00, /* reg. 0x0f */ + 0x00, /* reg. 0x10: may also be 0x0d */ + 0x00, /* reg. 0x11 */ + 0x1f, /* reg. 0x12: Set to maximum gain */ + 0x08, /* reg. 0x13: Set to Middle Gain: 0x08, + Low Gain: 0x00, High Gain: 0x10, enable IX2: 0x80 */ + 0x00, /* reg. 0x14 */ + 0x04, /* reg. 0x15: Enable LNA COMPS */ + }; + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + case FC_XTAL_28_8_MHZ: + reg[0x07] |= 0x20; + break; + case FC_XTAL_36_MHZ: + default: + break; + } + + if (priv->dual_master) + reg[0x0c] |= 0x02; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i < sizeof(reg); i++) { + ret = fc0012_writereg(priv, i, reg[i]); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (ret) + err("fc0012_writereg failed: %d", ret); + + return ret; +} + +static int fc0012_sleep(struct dvb_frontend *fe) +{ + /* nothing to do here */ + return 0; +} + +static int fc0012_set_params(struct dvb_frontend *fe) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int i, ret = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 freq = p->frequency / 1000; + u32 delsys = p->delivery_system; + unsigned char reg[7], am, pm, multi, tmp; + unsigned long f_vco; + unsigned short xtal_freq_khz_2, xin, xdiv; + int vco_select = false; + + if (fe->callback) { + ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); + if (ret) + goto exit; + } + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + xtal_freq_khz_2 = 27000 / 2; + break; + case FC_XTAL_36_MHZ: + xtal_freq_khz_2 = 36000 / 2; + break; + case FC_XTAL_28_8_MHZ: + default: + xtal_freq_khz_2 = 28800 / 2; + break; + } + + /* select frequency divider and the frequency of VCO */ + if (freq < 37084) { /* freq * 96 < 3560000 */ + multi = 96; + reg[5] = 0x82; + reg[6] = 0x00; + } else if (freq < 55625) { /* freq * 64 < 3560000 */ + multi = 64; + reg[5] = 0x82; + reg[6] = 0x02; + } else if (freq < 74167) { /* freq * 48 < 3560000 */ + multi = 48; + reg[5] = 0x42; + reg[6] = 0x00; + } else if (freq < 111250) { /* freq * 32 < 3560000 */ + multi = 32; + reg[5] = 0x42; + reg[6] = 0x02; + } else if (freq < 148334) { /* freq * 24 < 3560000 */ + multi = 24; + reg[5] = 0x22; + reg[6] = 0x00; + } else if (freq < 222500) { /* freq * 16 < 3560000 */ + multi = 16; + reg[5] = 0x22; + reg[6] = 0x02; + } else if (freq < 296667) { /* freq * 12 < 3560000 */ + multi = 12; + reg[5] = 0x12; + reg[6] = 0x00; + } else if (freq < 445000) { /* freq * 8 < 3560000 */ + multi = 8; + reg[5] = 0x12; + reg[6] = 0x02; + } else if (freq < 593334) { /* freq * 6 < 3560000 */ + multi = 6; + reg[5] = 0x0a; + reg[6] = 0x00; + } else { + multi = 4; + reg[5] = 0x0a; + reg[6] = 0x02; + } + + f_vco = freq * multi; + + if (f_vco >= 3060000) { + reg[6] |= 0x08; + vco_select = true; + } + + if (freq >= 45000) { + /* From divided value (XDIV) determined the FA and FP value */ + xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); + if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) + xdiv++; + + pm = (unsigned char)(xdiv / 8); + am = (unsigned char)(xdiv - (8 * pm)); + + if (am < 2) { + reg[1] = am + 8; + reg[2] = pm - 1; + } else { + reg[1] = am; + reg[2] = pm; + } + } else { + /* fix for frequency less than 45 MHz */ + reg[1] = 0x06; + reg[2] = 0x11; + } + + /* fix clock out */ + reg[6] |= 0x20; + + /* From VCO frequency determines the XIN ( fractional part of Delta + Sigma PLL) and divided value (XDIV) */ + xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); + xin = (xin << 15) / xtal_freq_khz_2; + if (xin >= 16384) + xin += 32768; + + reg[3] = xin >> 8; /* xin with 9 bit resolution */ + reg[4] = xin & 0xff; + + if (delsys == SYS_DVBT) { + reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ + switch (p->bandwidth_hz) { + case 6000000: + reg[6] |= 0x80; + break; + case 7000000: + reg[6] |= 0x40; + break; + case 8000000: + default: + break; + } + } else { + err("%s: modulation type not supported!", __func__); + return -EINVAL; + } + + /* modified for Realtek demod */ + reg[5] |= 0x07; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i <= 6; i++) { + ret = fc0012_writereg(priv, i, reg[i]); + if (ret) + goto exit; + } + + /* VCO Calibration */ + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + + /* VCO Re-Calibration if needed */ + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + + if (!ret) { + msleep(10); + ret = fc0012_readreg(priv, 0x0e, &tmp); + } + if (ret) + goto exit; + + /* vco selection */ + tmp &= 0x3f; + + if (vco_select) { + if (tmp > 0x3c) { + reg[6] &= ~0x08; + ret = fc0012_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + } + } else { + if (tmp < 0x02) { + reg[6] |= 0x08; + ret = fc0012_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + } + } + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static int fc0012_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0012_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int fc0012_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + /* CHECK: always ? */ + *frequency = 0; + return 0; +} + +static int fc0012_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0012_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +#define INPUT_ADC_LEVEL -8 + +static int fc0012_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int ret; + unsigned char tmp; + int int_temp, lna_gain, int_lna, tot_agc_gain, power; + const int fc0012_lna_gain_table[] = { + /* low gain */ + -63, -58, -99, -73, + -63, -65, -54, -60, + /* middle gain */ + 71, 70, 68, 67, + 65, 63, 61, 58, + /* high gain */ + 197, 191, 188, 186, + 184, 182, 181, 179, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0012_writereg(priv, 0x12, 0x00); + if (ret) + goto err; + + ret = fc0012_readreg(priv, 0x12, &tmp); + if (ret) + goto err; + int_temp = tmp; + + ret = fc0012_readreg(priv, 0x13, &tmp); + if (ret) + goto err; + lna_gain = tmp & 0x1f; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (lna_gain < ARRAY_SIZE(fc0012_lna_gain_table)) { + int_lna = fc0012_lna_gain_table[lna_gain]; + tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + + (int_temp & 0x1f)) * 2; + power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; + + if (power >= 45) + *strength = 255; /* 100% */ + else if (power < -95) + *strength = 0; + else + *strength = (power + 95) * 255 / 140; + + *strength |= *strength << 8; + } else { + ret = -1; + } + + goto exit; + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +exit: + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static const struct dvb_tuner_ops fc0012_tuner_ops = { + .info = { + .name = "Fitipower FC0012", + + .frequency_min = 37000000, /* estimate */ + .frequency_max = 862000000, /* estimate */ + .frequency_step = 0, + }, + + .release = fc0012_release, + + .init = fc0012_init, + .sleep = fc0012_sleep, + + .set_params = fc0012_set_params, + + .get_frequency = fc0012_get_frequency, + .get_if_frequency = fc0012_get_if_frequency, + .get_bandwidth = fc0012_get_bandwidth, + + .get_rf_strength = fc0012_get_rf_strength, +}; + +struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + struct fc0012_priv *priv = NULL; + + priv = kzalloc(sizeof(struct fc0012_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c = i2c; + priv->dual_master = dual_master; + priv->addr = i2c_address; + priv->xtal_freq = xtal_freq; + + info("Fitipower FC0012 successfully attached."); + + fe->tuner_priv = priv; + + memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + return fe; +} +EXPORT_SYMBOL(fc0012_attach); + +MODULE_DESCRIPTION("Fitipower FC0012 silicon tuner driver"); +MODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@gmx.net>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.6"); diff --git a/drivers/media/common/tuners/fc0012.h b/drivers/media/common/tuners/fc0012.h new file mode 100644 index 000000000000..4dbd5efe8845 --- /dev/null +++ b/drivers/media/common/tuners/fc0012.h @@ -0,0 +1,44 @@ +/* + * Fitipower FC0012 tuner driver - include + * + * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _FC0012_H_ +#define _FC0012_H_ + +#include "dvb_frontend.h" +#include "fc001x-common.h" + +#if defined(CONFIG_MEDIA_TUNER_FC0012) || \ + (defined(CONFIG_MEDIA_TUNER_FC0012_MODULE) && defined(MODULE)) +extern struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq); +#else +static inline struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/fc0013-priv.h b/drivers/media/common/tuners/fc0013-priv.h new file mode 100644 index 000000000000..bfd49dedea22 --- /dev/null +++ b/drivers/media/common/tuners/fc0013-priv.h @@ -0,0 +1,44 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _FC0013_PRIV_H_ +#define _FC0013_PRIV_H_ + +#define LOG_PREFIX "fc0013" + +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + +struct fc0013_priv { + struct i2c_adapter *i2c; + u8 addr; + u8 dual_master; + u8 xtal_freq; + + u32 frequency; + u32 bandwidth; +}; + +#endif diff --git a/drivers/media/common/tuners/fc0013.c b/drivers/media/common/tuners/fc0013.c new file mode 100644 index 000000000000..bd8f0f1e8f3b --- /dev/null +++ b/drivers/media/common/tuners/fc0013.c @@ -0,0 +1,634 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> + * partially based on driver code from Fitipower + * Copyright (C) 2010 Fitipower Integrated Technology Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "fc0013.h" +#include "fc0013-priv.h" + +static int fc0013_writereg(struct fc0013_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = {reg, val}; + struct i2c_msg msg = { + .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 + }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + err("I2C write reg failed, reg: %02x, val: %02x", reg, val); + return -EREMOTEIO; + } + return 0; +} + +static int fc0013_readreg(struct fc0013_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + err("I2C read reg failed, reg: %02x", reg); + return -EREMOTEIO; + } + return 0; +} + +static int fc0013_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int fc0013_init(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int i, ret = 0; + unsigned char reg[] = { + 0x00, /* reg. 0x00: dummy */ + 0x09, /* reg. 0x01 */ + 0x16, /* reg. 0x02 */ + 0x00, /* reg. 0x03 */ + 0x00, /* reg. 0x04 */ + 0x17, /* reg. 0x05 */ + 0x02, /* reg. 0x06 */ + 0x0a, /* reg. 0x07: CHECK */ + 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, + Loop Bw 1/8 */ + 0x6f, /* reg. 0x09: enable LoopThrough */ + 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ + 0x82, /* reg. 0x0b: CHECK */ + 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ + 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */ + 0x00, /* reg. 0x0e */ + 0x00, /* reg. 0x0f */ + 0x00, /* reg. 0x10 */ + 0x00, /* reg. 0x11 */ + 0x00, /* reg. 0x12 */ + 0x00, /* reg. 0x13 */ + 0x50, /* reg. 0x14: DVB-t High Gain, UHF. + Middle Gain: 0x48, Low Gain: 0x40 */ + 0x01, /* reg. 0x15 */ + }; + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + case FC_XTAL_28_8_MHZ: + reg[0x07] |= 0x20; + break; + case FC_XTAL_36_MHZ: + default: + break; + } + + if (priv->dual_master) + reg[0x0c] |= 0x02; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i < sizeof(reg); i++) { + ret = fc0013_writereg(priv, i, reg[i]); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (ret) + err("fc0013_writereg failed: %d", ret); + + return ret; +} + +static int fc0013_sleep(struct dvb_frontend *fe) +{ + /* nothing to do here */ + return 0; +} + +int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + u8 rc_cal; + int val; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + /* push rc_cal value, get rc_cal value */ + ret = fc0013_writereg(priv, 0x10, 0x00); + if (ret) + goto error_out; + + /* get rc_cal value */ + ret = fc0013_readreg(priv, 0x10, &rc_cal); + if (ret) + goto error_out; + + rc_cal &= 0x0f; + + val = (int)rc_cal + rc_val; + + /* forcing rc_cal */ + ret = fc0013_writereg(priv, 0x0d, 0x11); + if (ret) + goto error_out; + + /* modify rc_cal value */ + if (val > 15) + ret = fc0013_writereg(priv, 0x10, 0x0f); + else if (val < 0) + ret = fc0013_writereg(priv, 0x10, 0x00); + else + ret = fc0013_writereg(priv, 0x10, (u8)val); + +error_out: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + return ret; +} +EXPORT_SYMBOL(fc0013_rc_cal_add); + +int fc0013_rc_cal_reset(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0013_writereg(priv, 0x0d, 0x01); + if (!ret) + ret = fc0013_writereg(priv, 0x10, 0x00); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + return ret; +} +EXPORT_SYMBOL(fc0013_rc_cal_reset); + +static int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq) +{ + int ret; + u8 tmp; + + ret = fc0013_readreg(priv, 0x1d, &tmp); + if (ret) + goto error_out; + tmp &= 0xe3; + if (freq <= 177500) { /* VHF Track: 7 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); + } else if (freq <= 184500) { /* VHF Track: 6 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x18); + } else if (freq <= 191500) { /* VHF Track: 5 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x14); + } else if (freq <= 198500) { /* VHF Track: 4 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x10); + } else if (freq <= 205500) { /* VHF Track: 3 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x0c); + } else if (freq <= 219500) { /* VHF Track: 2 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x08); + } else if (freq < 300000) { /* VHF Track: 1 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x04); + } else { /* UHF and GPS */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); + } + if (ret) + goto error_out; +error_out: + return ret; +} + +static int fc0013_set_params(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int i, ret = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 freq = p->frequency / 1000; + u32 delsys = p->delivery_system; + unsigned char reg[7], am, pm, multi, tmp; + unsigned long f_vco; + unsigned short xtal_freq_khz_2, xin, xdiv; + int vco_select = false; + + if (fe->callback) { + ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); + if (ret) + goto exit; + } + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + xtal_freq_khz_2 = 27000 / 2; + break; + case FC_XTAL_36_MHZ: + xtal_freq_khz_2 = 36000 / 2; + break; + case FC_XTAL_28_8_MHZ: + default: + xtal_freq_khz_2 = 28800 / 2; + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + /* set VHF track */ + ret = fc0013_set_vhf_track(priv, freq); + if (ret) + goto exit; + + if (freq < 300000) { + /* enable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp | 0x10); + if (ret) + goto exit; + + /* disable UHF & disable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, tmp & 0x1f); + if (ret) + goto exit; + } else if (freq <= 862000) { + /* disable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp & 0xef); + if (ret) + goto exit; + + /* enable UHF & disable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x40); + if (ret) + goto exit; + } else { + /* disable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp & 0xef); + if (ret) + goto exit; + + /* disable UHF & enable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x20); + if (ret) + goto exit; + } + + /* select frequency divider and the frequency of VCO */ + if (freq < 37084) { /* freq * 96 < 3560000 */ + multi = 96; + reg[5] = 0x82; + reg[6] = 0x00; + } else if (freq < 55625) { /* freq * 64 < 3560000 */ + multi = 64; + reg[5] = 0x02; + reg[6] = 0x02; + } else if (freq < 74167) { /* freq * 48 < 3560000 */ + multi = 48; + reg[5] = 0x42; + reg[6] = 0x00; + } else if (freq < 111250) { /* freq * 32 < 3560000 */ + multi = 32; + reg[5] = 0x82; + reg[6] = 0x02; + } else if (freq < 148334) { /* freq * 24 < 3560000 */ + multi = 24; + reg[5] = 0x22; + reg[6] = 0x00; + } else if (freq < 222500) { /* freq * 16 < 3560000 */ + multi = 16; + reg[5] = 0x42; + reg[6] = 0x02; + } else if (freq < 296667) { /* freq * 12 < 3560000 */ + multi = 12; + reg[5] = 0x12; + reg[6] = 0x00; + } else if (freq < 445000) { /* freq * 8 < 3560000 */ + multi = 8; + reg[5] = 0x22; + reg[6] = 0x02; + } else if (freq < 593334) { /* freq * 6 < 3560000 */ + multi = 6; + reg[5] = 0x0a; + reg[6] = 0x00; + } else if (freq < 950000) { /* freq * 4 < 3800000 */ + multi = 4; + reg[5] = 0x12; + reg[6] = 0x02; + } else { + multi = 2; + reg[5] = 0x0a; + reg[6] = 0x02; + } + + f_vco = freq * multi; + + if (f_vco >= 3060000) { + reg[6] |= 0x08; + vco_select = true; + } + + if (freq >= 45000) { + /* From divided value (XDIV) determined the FA and FP value */ + xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); + if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) + xdiv++; + + pm = (unsigned char)(xdiv / 8); + am = (unsigned char)(xdiv - (8 * pm)); + + if (am < 2) { + reg[1] = am + 8; + reg[2] = pm - 1; + } else { + reg[1] = am; + reg[2] = pm; + } + } else { + /* fix for frequency less than 45 MHz */ + reg[1] = 0x06; + reg[2] = 0x11; + } + + /* fix clock out */ + reg[6] |= 0x20; + + /* From VCO frequency determines the XIN ( fractional part of Delta + Sigma PLL) and divided value (XDIV) */ + xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); + xin = (xin << 15) / xtal_freq_khz_2; + if (xin >= 16384) + xin += 32768; + + reg[3] = xin >> 8; + reg[4] = xin & 0xff; + + if (delsys == SYS_DVBT) { + reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ + switch (p->bandwidth_hz) { + case 6000000: + reg[6] |= 0x80; + break; + case 7000000: + reg[6] |= 0x40; + break; + case 8000000: + default: + break; + } + } else { + err("%s: modulation type not supported!", __func__); + return -EINVAL; + } + + /* modified for Realtek demod */ + reg[5] |= 0x07; + + for (i = 1; i <= 6; i++) { + ret = fc0013_writereg(priv, i, reg[i]); + if (ret) + goto exit; + } + + ret = fc0013_readreg(priv, 0x11, &tmp); + if (ret) + goto exit; + if (multi == 64) + ret = fc0013_writereg(priv, 0x11, tmp | 0x04); + else + ret = fc0013_writereg(priv, 0x11, tmp & 0xfb); + if (ret) + goto exit; + + /* VCO Calibration */ + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + + /* VCO Re-Calibration if needed */ + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + + if (!ret) { + msleep(10); + ret = fc0013_readreg(priv, 0x0e, &tmp); + } + if (ret) + goto exit; + + /* vco selection */ + tmp &= 0x3f; + + if (vco_select) { + if (tmp > 0x3c) { + reg[6] &= ~0x08; + ret = fc0013_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + } + } else { + if (tmp < 0x02) { + reg[6] |= 0x08; + ret = fc0013_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + } + } + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static int fc0013_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0013_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int fc0013_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + /* always ? */ + *frequency = 0; + return 0; +} + +static int fc0013_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0013_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +#define INPUT_ADC_LEVEL -8 + +static int fc0013_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + unsigned char tmp; + int int_temp, lna_gain, int_lna, tot_agc_gain, power; + const int fc0013_lna_gain_table[] = { + /* low gain */ + -63, -58, -99, -73, + -63, -65, -54, -60, + /* middle gain */ + 71, 70, 68, 67, + 65, 63, 61, 58, + /* high gain */ + 197, 191, 188, 186, + 184, 182, 181, 179, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0013_writereg(priv, 0x13, 0x00); + if (ret) + goto err; + + ret = fc0013_readreg(priv, 0x13, &tmp); + if (ret) + goto err; + int_temp = tmp; + + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto err; + lna_gain = tmp & 0x1f; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (lna_gain < ARRAY_SIZE(fc0013_lna_gain_table)) { + int_lna = fc0013_lna_gain_table[lna_gain]; + tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + + (int_temp & 0x1f)) * 2; + power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; + + if (power >= 45) + *strength = 255; /* 100% */ + else if (power < -95) + *strength = 0; + else + *strength = (power + 95) * 255 / 140; + + *strength |= *strength << 8; + } else { + ret = -1; + } + + goto exit; + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +exit: + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static const struct dvb_tuner_ops fc0013_tuner_ops = { + .info = { + .name = "Fitipower FC0013", + + .frequency_min = 37000000, /* estimate */ + .frequency_max = 1680000000, /* CHECK */ + .frequency_step = 0, + }, + + .release = fc0013_release, + + .init = fc0013_init, + .sleep = fc0013_sleep, + + .set_params = fc0013_set_params, + + .get_frequency = fc0013_get_frequency, + .get_if_frequency = fc0013_get_if_frequency, + .get_bandwidth = fc0013_get_bandwidth, + + .get_rf_strength = fc0013_get_rf_strength, +}; + +struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + struct fc0013_priv *priv = NULL; + + priv = kzalloc(sizeof(struct fc0013_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c = i2c; + priv->dual_master = dual_master; + priv->addr = i2c_address; + priv->xtal_freq = xtal_freq; + + info("Fitipower FC0013 successfully attached."); + + fe->tuner_priv = priv; + + memcpy(&fe->ops.tuner_ops, &fc0013_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + return fe; +} +EXPORT_SYMBOL(fc0013_attach); + +MODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver"); +MODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@gmx.net>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.2"); diff --git a/drivers/media/common/tuners/fc0013.h b/drivers/media/common/tuners/fc0013.h new file mode 100644 index 000000000000..594efd64aeec --- /dev/null +++ b/drivers/media/common/tuners/fc0013.h @@ -0,0 +1,57 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _FC0013_H_ +#define _FC0013_H_ + +#include "dvb_frontend.h" +#include "fc001x-common.h" + +#if defined(CONFIG_MEDIA_TUNER_FC0013) || \ + (defined(CONFIG_MEDIA_TUNER_FC0013_MODULE) && defined(MODULE)) +extern struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq); +extern int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val); +extern int fc0013_rc_cal_reset(struct dvb_frontend *fe); +#else +static inline struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val) +{ + return 0; +} + +static inline int fc0013_rc_cal_reset(struct dvb_frontend *fe) +{ + return 0; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/fc001x-common.h b/drivers/media/common/tuners/fc001x-common.h new file mode 100644 index 000000000000..718818156934 --- /dev/null +++ b/drivers/media/common/tuners/fc001x-common.h @@ -0,0 +1,39 @@ +/* + * Fitipower FC0012 & FC0013 tuner driver - common defines + * + * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _FC001X_COMMON_H_ +#define _FC001X_COMMON_H_ + +enum fc001x_xtal_freq { + FC_XTAL_27_MHZ, /* 27000000 */ + FC_XTAL_28_8_MHZ, /* 28800000 */ + FC_XTAL_36_MHZ, /* 36000000 */ +}; + +/* + * enum fc001x_fe_callback_commands - Frontend callbacks + * + * @FC_FE_CALLBACK_VHF_ENABLE: enable VHF or UHF + */ +enum fc001x_fe_callback_commands { + FC_FE_CALLBACK_VHF_ENABLE, +}; + +#endif diff --git a/drivers/media/common/tuners/tua9001.c b/drivers/media/common/tuners/tua9001.c new file mode 100644 index 000000000000..de2607084672 --- /dev/null +++ b/drivers/media/common/tuners/tua9001.c @@ -0,0 +1,215 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "tua9001.h" +#include "tua9001_priv.h" + +/* write register */ +static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val) +{ + int ret; + u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff }; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg->i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + printk(KERN_WARNING "%s: I2C wr failed=%d reg=%02x\n", + __func__, ret, reg); + ret = -EREMOTEIO; + } + + return ret; +} + +static int tua9001_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static int tua9001_init(struct dvb_frontend *fe) +{ + struct tua9001_priv *priv = fe->tuner_priv; + int ret = 0; + u8 i; + struct reg_val data[] = { + { 0x1e, 0x6512 }, + { 0x25, 0xb888 }, + { 0x39, 0x5460 }, + { 0x3b, 0x00c0 }, + { 0x3a, 0xf000 }, + { 0x08, 0x0000 }, + { 0x32, 0x0030 }, + { 0x41, 0x703a }, + { 0x40, 0x1c78 }, + { 0x2c, 0x1c00 }, + { 0x36, 0xc013 }, + { 0x37, 0x6f18 }, + { 0x27, 0x0008 }, + { 0x2a, 0x0001 }, + { 0x34, 0x0a40 }, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ + + if (ret < 0) + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int tua9001_set_params(struct dvb_frontend *fe) +{ + struct tua9001_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + u16 val; + u32 frequency; + struct reg_val data[2]; + + pr_debug("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", + __func__, c->delivery_system, c->frequency, + c->bandwidth_hz); + + switch (c->delivery_system) { + case SYS_DVBT: + switch (c->bandwidth_hz) { + case 8000000: + val = 0x0000; + break; + case 7000000: + val = 0x1000; + break; + case 6000000: + val = 0x2000; + break; + case 5000000: + val = 0x3000; + break; + default: + ret = -EINVAL; + goto err; + } + break; + default: + ret = -EINVAL; + goto err; + } + + data[0].reg = 0x04; + data[0].val = val; + + frequency = (c->frequency - 150000000); + frequency /= 100; + frequency *= 48; + frequency /= 10000; + + data[1].reg = 0x1f; + data[1].val = frequency; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); + if (ret < 0) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ + +err: + if (ret < 0) + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 0; /* Zero-IF */ + + return 0; +} + +static const struct dvb_tuner_ops tua9001_tuner_ops = { + .info = { + .name = "Infineon TUA 9001", + + .frequency_min = 170000000, + .frequency_max = 862000000, + .frequency_step = 0, + }, + + .release = tua9001_release, + + .init = tua9001_init, + .set_params = tua9001_set_params, + + .get_if_frequency = tua9001_get_if_frequency, +}; + +struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg) +{ + struct tua9001_priv *priv = NULL; + + priv = kzalloc(sizeof(struct tua9001_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + + printk(KERN_INFO "Infineon TUA 9001 successfully attached."); + + memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + return fe; +} +EXPORT_SYMBOL(tua9001_attach); + +MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/tua9001.h b/drivers/media/common/tuners/tua9001.h new file mode 100644 index 000000000000..38d6ae76b1d6 --- /dev/null +++ b/drivers/media/common/tuners/tua9001.h @@ -0,0 +1,46 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TUA9001_H +#define TUA9001_H + +#include "dvb_frontend.h" + +struct tua9001_config { + /* + * I2C address + */ + u8 i2c_addr; +}; + +#if defined(CONFIG_MEDIA_TUNER_TUA9001) || \ + (defined(CONFIG_MEDIA_TUNER_TUA9001_MODULE) && defined(MODULE)) +extern struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg); +#else +static inline struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/tua9001_priv.h b/drivers/media/common/tuners/tua9001_priv.h new file mode 100644 index 000000000000..73cc1ce0575c --- /dev/null +++ b/drivers/media/common/tuners/tua9001_priv.h @@ -0,0 +1,34 @@ +/* + * Infineon TUA 9001 silicon tuner driver + * + * Copyright (C) 2009 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TUA9001_PRIV_H +#define TUA9001_PRIV_H + +struct reg_val { + u8 reg; + u16 val; +}; + +struct tua9001_priv { + struct tua9001_config *cfg; + struct i2c_adapter *i2c; +}; + +#endif diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index eab2ea424200..dcca42ca57be 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -54,7 +54,7 @@ struct xc5000_priv { struct list_head hybrid_tuner_instance_list; u32 if_khz; - u32 xtal_khz; + u16 xtal_khz; u32 freq_hz; u32 bandwidth; u8 video_standard; @@ -631,7 +631,10 @@ static int xc5000_fwupload(struct dvb_frontend *fe) ret = xc_load_i2c_sequence(fe, fw->data); if (XC_RESULT_SUCCESS == ret) ret = xc_set_xtal(fe); - printk(KERN_INFO "xc5000: firmware upload complete...\n"); + if (XC_RESULT_SUCCESS == ret) + printk(KERN_INFO "xc5000: firmware upload complete...\n"); + else + printk(KERN_ERR "xc5000: firmware upload failed...\n"); } out: diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index 39a73bf01406..b1a547494625 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -34,7 +34,7 @@ struct xc5000_config { u8 i2c_address; u32 if_khz; u8 radio_input; - u32 xtal_khz; + u16 xtal_khz; int chip_id; }; |