Commit fc3a0497 authored by Antti Palosaari's avatar Antti Palosaari Committed by Mauro Carvalho Chehab

[media] rtl2832_sdr: add support for fc2580 tuner

Add initial support for fc2580 tuner based devices.
Tuner is controlled via V4L2 subdevice API.
Passes v4l2-compliance tests.
Signed-off-by: default avatarAntti Palosaari <crope@iki.fi>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@osg.samsung.com>
parent 252fad1c
......@@ -39,6 +39,10 @@ static bool rtl2832_sdr_emulated_fmt;
module_param_named(emulated_formats, rtl2832_sdr_emulated_fmt, bool, 0644);
MODULE_PARM_DESC(emulated_formats, "enable emulated formats (disappears in future)");
/* Original macro does not contain enough null pointer checks for our need */
#define V4L2_SUBDEV_HAS_OP(sd, o, f) \
((sd) && (sd)->ops && (sd)->ops->o && (sd)->ops->o->f)
#define MAX_BULK_BUFS (10)
#define BULK_BUFFER_SIZE (128 * 512)
......@@ -116,6 +120,7 @@ struct rtl2832_sdr_dev {
struct video_device vdev;
struct v4l2_device v4l2_dev;
struct v4l2_subdev *v4l2_subdev;
/* videobuf2 queue and queued buffers list */
struct vb2_queue vb_queue;
......@@ -742,6 +747,29 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev)
ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xf4", 1);
break;
case RTL2832_SDR_TUNER_FC2580:
ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x39", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x2c", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x16", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x9c", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xe9\xf4", 2);
break;
default:
dev_notice(&pdev->dev, "Unsupported tuner\n");
}
......@@ -832,8 +860,10 @@ static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_dev *dev)
if (!test_bit(POWER_ON, &dev->flags))
return 0;
if (!V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_frequency)) {
if (fe->ops.tuner_ops.set_params)
fe->ops.tuner_ops.set_params(fe);
}
return 0;
};
......@@ -891,6 +921,10 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
set_bit(POWER_ON, &dev->flags);
/* wake-up tuner */
if (V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, core, s_power))
ret = v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1);
else
ret = rtl2832_sdr_set_tuner(dev);
if (ret)
goto err;
......@@ -939,6 +973,11 @@ static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
rtl2832_sdr_free_stream_bufs(dev);
rtl2832_sdr_cleanup_queued_bufs(dev);
rtl2832_sdr_unset_adc(dev);
/* sleep tuner */
if (V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, core, s_power))
v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 0);
else
rtl2832_sdr_unset_tuner(dev);
clear_bit(POWER_ON, &dev->flags);
......@@ -968,6 +1007,7 @@ static int rtl2832_sdr_g_tuner(struct file *file, void *priv,
{
struct rtl2832_sdr_dev *dev = video_drvdata(file);
struct platform_device *pdev = dev->pdev;
int ret;
dev_dbg(&pdev->dev, "index=%d type=%d\n", v->index, v->type);
......@@ -977,17 +1017,21 @@ static int rtl2832_sdr_g_tuner(struct file *file, void *priv,
v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
v->rangelow = 300000;
v->rangehigh = 3200000;
ret = 0;
} else if (v->index == 1 &&
V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, g_tuner)) {
ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, g_tuner, v);
} else if (v->index == 1) {
strlcpy(v->name, "RF: <unknown>", sizeof(v->name));
v->type = V4L2_TUNER_RF;
v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
v->rangelow = 50000000;
v->rangehigh = 2000000000;
ret = 0;
} else {
return -EINVAL;
ret = -EINVAL;
}
return 0;
return ret;
}
static int rtl2832_sdr_s_tuner(struct file *file, void *priv,
......@@ -995,12 +1039,21 @@ static int rtl2832_sdr_s_tuner(struct file *file, void *priv,
{
struct rtl2832_sdr_dev *dev = video_drvdata(file);
struct platform_device *pdev = dev->pdev;
int ret;
dev_dbg(&pdev->dev, "\n");
if (v->index > 1)
return -EINVAL;
return 0;
if (v->index == 0) {
ret = 0;
} else if (v->index == 1 &&
V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_tuner)) {
ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, s_tuner, v);
} else if (v->index == 1) {
ret = 0;
} else {
ret = -EINVAL;
}
return ret;
}
static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv,
......@@ -1008,6 +1061,7 @@ static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv,
{
struct rtl2832_sdr_dev *dev = video_drvdata(file);
struct platform_device *pdev = dev->pdev;
int ret;
dev_dbg(&pdev->dev, "tuner=%d type=%d index=%d\n",
band->tuner, band->type, band->index);
......@@ -1017,16 +1071,20 @@ static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv,
return -EINVAL;
*band = bands_adc[band->index];
ret = 0;
} else if (band->tuner == 1 &&
V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, enum_freq_bands)) {
ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, enum_freq_bands, band);
} else if (band->tuner == 1) {
if (band->index >= ARRAY_SIZE(bands_fm))
return -EINVAL;
*band = bands_fm[band->index];
ret = 0;
} else {
return -EINVAL;
ret = -EINVAL;
}
return 0;
return ret;
}
static int rtl2832_sdr_g_frequency(struct file *file, void *priv,
......@@ -1034,20 +1092,25 @@ static int rtl2832_sdr_g_frequency(struct file *file, void *priv,
{
struct rtl2832_sdr_dev *dev = video_drvdata(file);
struct platform_device *pdev = dev->pdev;
int ret = 0;
int ret;
dev_dbg(&pdev->dev, "tuner=%d type=%d\n", f->tuner, f->type);
if (f->tuner == 0) {
f->frequency = dev->f_adc;
f->type = V4L2_TUNER_ADC;
ret = 0;
} else if (f->tuner == 1 &&
V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, g_frequency)) {
f->type = V4L2_TUNER_RF;
ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, g_frequency, f);
} else if (f->tuner == 1) {
f->frequency = dev->f_tuner;
f->type = V4L2_TUNER_RF;
ret = 0;
} else {
return -EINVAL;
ret = -EINVAL;
}
return ret;
}
......@@ -1079,6 +1142,9 @@ static int rtl2832_sdr_s_frequency(struct file *file, void *priv,
dev_dbg(&pdev->dev, "ADC frequency=%u Hz\n", dev->f_adc);
ret = rtl2832_sdr_set_adc(dev);
} else if (f->tuner == 1 &&
V4L2_SUBDEV_HAS_OP(dev->v4l2_subdev, tuner, s_frequency)) {
ret = v4l2_subdev_call(dev->v4l2_subdev, tuner, s_frequency, f);
} else if (f->tuner == 1) {
dev->f_tuner = clamp_t(unsigned int, f->frequency,
bands_fm[0].rangelow,
......@@ -1089,7 +1155,6 @@ static int rtl2832_sdr_s_frequency(struct file *file, void *priv,
} else {
ret = -EINVAL;
}
return ret;
}
......@@ -1329,6 +1394,7 @@ static int rtl2832_sdr_probe(struct platform_device *pdev)
/* setup the state */
subdev = pdata->v4l2_subdev;
dev->v4l2_subdev = pdata->v4l2_subdev;
dev->pdev = pdev;
dev->udev = pdata->dvb_usb_device->udev;
dev->f_adc = bands_adc[0].rangelow;
......@@ -1388,6 +1454,12 @@ static int rtl2832_sdr_probe(struct platform_device *pdev)
6000000);
v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false);
break;
case RTL2832_SDR_TUNER_FC2580:
v4l2_ctrl_handler_init(&dev->hdl, 2);
if (subdev)
v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler,
NULL);
break;
default:
v4l2_ctrl_handler_init(&dev->hdl, 0);
dev_err(&pdev->dev, "Unsupported tuner\n");
......
......@@ -47,6 +47,7 @@ struct rtl2832_sdr_platform_data {
/*
* XXX: This list must be kept sync with dvb_usb_rtl28xxu USB IF driver.
*/
#define RTL2832_SDR_TUNER_FC2580 0x21
#define RTL2832_SDR_TUNER_TUA9001 0x24
#define RTL2832_SDR_TUNER_FC0012 0x26
#define RTL2832_SDR_TUNER_E4000 0x27
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment