diff options
-rw-r--r-- | drivers/media/platform/vivid/Makefile | 4 | ||||
-rw-r--r-- | drivers/media/platform/vivid/vivid-core.c | 123 | ||||
-rw-r--r-- | drivers/media/platform/vivid/vivid-core.h | 42 | ||||
-rw-r--r-- | drivers/media/platform/vivid/vivid-radio-tx.c | 4 | ||||
-rw-r--r-- | drivers/media/platform/vivid/vivid-radio-tx.h | 4 | ||||
-rw-r--r-- | drivers/media/platform/vivid/vivid-sdr-cap.c | 135 | ||||
-rw-r--r-- | drivers/media/platform/vivid/vivid-sdr-cap.h | 7 | ||||
-rw-r--r-- | drivers/media/platform/vivid/vivid-sdr-common.c | 146 | ||||
-rw-r--r-- | drivers/media/platform/vivid/vivid-sdr-common.h | 35 | ||||
-rw-r--r-- | drivers/media/platform/vivid/vivid-sdr-out.c | 499 | ||||
-rw-r--r-- | drivers/media/platform/vivid/vivid-sdr-out.h | 33 |
11 files changed, 894 insertions, 138 deletions
diff --git a/drivers/media/platform/vivid/Makefile b/drivers/media/platform/vivid/Makefile index 756fc12851df..597b1f8dd379 100644 --- a/drivers/media/platform/vivid/Makefile +++ b/drivers/media/platform/vivid/Makefile @@ -1,6 +1,6 @@ vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \ vivid-vid-cap.o vivid-vid-out.o vivid-kthread-cap.o vivid-kthread-out.o \ vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \ - vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \ - vivid-osd.o vivid-tpg.o vivid-tpg-colors.o + vivid-rds-gen.o vivid-sdr-common.o vivid-sdr-cap.o vivid-sdr-out.o \ + vivid-vbi-cap.o vivid-vbi-out.o vivid-osd.o vivid-tpg.o vivid-tpg-colors.o obj-$(CONFIG_VIDEO_VIVID) += vivid.o diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index ec125becb7af..5bd97d62c25c 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -42,7 +42,9 @@ #include "vivid-radio-common.h" #include "vivid-radio-rx.h" #include "vivid-radio-tx.h" +#include "vivid-sdr-common.h" #include "vivid-sdr-cap.h" +#include "vivid-sdr-out.h" #include "vivid-vbi-cap.h" #include "vivid-vbi-out.h" #include "vivid-osd.h" @@ -81,6 +83,10 @@ static int sdr_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; module_param_array(sdr_cap_nr, int, NULL, 0444); MODULE_PARM_DESC(sdr_cap_nr, " swradioX start number, -1 is autodetect"); +static int sdr_out_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; +module_param_array(sdr_out_nr, int, NULL, 0444); +MODULE_PARM_DESC(sdr_out_nr, " swradioX start number, -1 is autodetect"); + static int radio_rx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; module_param_array(radio_rx_nr, int, NULL, 0444); MODULE_PARM_DESC(radio_rx_nr, " radioX start number, -1 is autodetect"); @@ -105,8 +111,8 @@ static unsigned multiplanar[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 1 module_param_array(multiplanar, uint, NULL, 0444); MODULE_PARM_DESC(multiplanar, " 1 (default) creates a single planar device, 2 creates a multiplanar device."); -/* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + vbi-out + vid-out */ -static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x1d3d }; +/* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr-cap + sdr-out + vbi-out + vid-out */ +static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x3d3d }; module_param_array(node_types, uint, NULL, 0444); MODULE_PARM_DESC(node_types, " node types, default is 0x1d3d. Bitmask with the following meaning:\n" "\t\t bit 0: Video Capture node\n" @@ -116,6 +122,7 @@ MODULE_PARM_DESC(node_types, " node types, default is 0x1d3d. Bitmask with the f "\t\t bit 8: Video Output node\n" "\t\t bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n" "\t\t bit 12: Radio Transmitter node\n" + "\t\t bit 13: Software Defined Radio Receiver node\n" "\t\t bit 16: Framebuffer for testing overlays"); /* Default: 4 inputs */ @@ -215,8 +222,10 @@ static int vidioc_querycap(struct file *file, void *priv, cap->device_caps = dev->vbi_cap_caps; else if (vdev->vfl_type == VFL_TYPE_VBI && vdev->vfl_dir == VFL_DIR_TX) cap->device_caps = dev->vbi_out_caps; - else if (vdev->vfl_type == VFL_TYPE_SDR) + else if (vdev->vfl_type == VFL_TYPE_SDR && vdev->vfl_dir == VFL_DIR_RX) cap->device_caps = dev->sdr_cap_caps; + else if (vdev->vfl_type == VFL_TYPE_SDR && vdev->vfl_dir == VFL_DIR_TX) + cap->device_caps = dev->sdr_out_caps; else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_RX) cap->device_caps = dev->radio_rx_caps; else if (vdev->vfl_type == VFL_TYPE_RADIO && vdev->vfl_dir == VFL_DIR_TX) @@ -224,7 +233,7 @@ static int vidioc_querycap(struct file *file, void *priv, cap->capabilities = dev->vid_cap_caps | dev->vid_out_caps | dev->vbi_cap_caps | dev->vbi_out_caps | dev->radio_rx_caps | dev->radio_tx_caps | - dev->sdr_cap_caps | V4L2_CAP_DEVICE_CAPS; + dev->sdr_cap_caps | dev->sdr_out_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -270,6 +279,28 @@ static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner * return vivid_video_s_tuner(file, fh, vt); } +static int vidioc_g_modulator(struct file *file, void *fh, struct v4l2_modulator *mt) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_g_modulator(file, fh, mt); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_g_modulator(file, fh, mt); + return -ENOTTY; +} + +static int vidioc_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *mt) +{ + struct video_device *vdev = video_devdata(file); + + if (vdev->vfl_type == VFL_TYPE_RADIO) + return vivid_radio_s_modulator(file, fh, mt); + if (vdev->vfl_type == VFL_TYPE_SDR) + return vivid_sdr_s_modulator(file, fh, mt); + return -ENOTTY; +} + static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) { struct vivid_dev *dev = video_drvdata(file); @@ -280,7 +311,9 @@ static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency vdev->vfl_dir == VFL_DIR_RX ? &dev->radio_rx_freq : &dev->radio_tx_freq, vf); if (vdev->vfl_type == VFL_TYPE_SDR) - return vivid_sdr_g_frequency(file, fh, vf); + return vdev->vfl_dir == VFL_DIR_RX ? + vivid_sdr_cap_g_frequency(file, fh, vf) : + vivid_sdr_out_g_frequency(file, fh, vf); return vivid_video_g_frequency(file, fh, vf); } @@ -294,7 +327,9 @@ static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_fre vdev->vfl_dir == VFL_DIR_RX ? &dev->radio_rx_freq : &dev->radio_tx_freq, vf); if (vdev->vfl_type == VFL_TYPE_SDR) - return vivid_sdr_s_frequency(file, fh, vf); + return vdev->vfl_dir == VFL_DIR_RX ? + vivid_sdr_cap_s_frequency(file, fh, vf) : + vivid_sdr_out_s_frequency(file, fh, vf); return vivid_video_s_frequency(file, fh, vf); } @@ -450,6 +485,7 @@ static bool vivid_is_last_user(struct vivid_dev *dev) vivid_is_in_use(&dev->vbi_cap_dev) + vivid_is_in_use(&dev->vbi_out_dev) + vivid_is_in_use(&dev->sdr_cap_dev) + + vivid_is_in_use(&dev->sdr_out_dev) + vivid_is_in_use(&dev->radio_rx_dev) + vivid_is_in_use(&dev->radio_tx_dev); @@ -475,6 +511,7 @@ static int vivid_fop_release(struct file *file) set_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags); set_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags); set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); + set_bit(V4L2_FL_REGISTERED, &dev->sdr_out_dev.flags); set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); } @@ -557,11 +594,16 @@ static const struct v4l2_ioctl_ops vivid_ioctl_ops = { .vidioc_try_fmt_sliced_vbi_out = vidioc_try_fmt_sliced_vbi_out, .vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out, - .vidioc_enum_fmt_sdr_cap = vidioc_enum_fmt_sdr_cap, + .vidioc_enum_fmt_sdr_cap = vivid_enum_fmt_sdr, .vidioc_g_fmt_sdr_cap = vidioc_g_fmt_sdr_cap, - .vidioc_try_fmt_sdr_cap = vidioc_try_fmt_sdr_cap, + .vidioc_try_fmt_sdr_cap = vivid_try_fmt_sdr, .vidioc_s_fmt_sdr_cap = vidioc_s_fmt_sdr_cap, + .vidioc_enum_fmt_sdr_out = vivid_enum_fmt_sdr, + .vidioc_g_fmt_sdr_out = vidioc_g_fmt_sdr_out, + .vidioc_try_fmt_sdr_out = vivid_try_fmt_sdr, + .vidioc_s_fmt_sdr_out = vidioc_s_fmt_sdr_out, + .vidioc_overlay = vidioc_overlay, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_enum_frameintervals = vidioc_enum_frameintervals, @@ -741,12 +783,15 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) /* do we create a software defined radio capture device? */ dev->has_sdr_cap = node_type & 0x0020; + /* do we create a software defined radio output device? */ + dev->has_sdr_out = node_type & 0x2000; + /* do we have a tuner? */ has_tuner = ((dev->has_vid_cap || dev->has_vbi_cap) && in_type_counter[TV]) || dev->has_radio_rx || dev->has_sdr_cap; /* do we have a modulator? */ - has_modulator = dev->has_radio_tx; + has_modulator = dev->has_radio_tx || dev->has_sdr_out; if (dev->has_vid_cap) /* do we have a framebuffer for overlay testing? */ @@ -829,6 +874,11 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->sdr_cap_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER; dev->sdr_cap_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; } + if (dev->has_sdr_out) { + /* set up the capabilities of the sdr output device */ + dev->sdr_out_caps = V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR; + dev->sdr_out_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + } /* set up the capabilities of the radio receiver device */ if (dev->has_radio_rx) dev->radio_rx_caps = V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE | @@ -975,10 +1025,12 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->radio_rds_loop = false; } dev->radio_tx_subchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS; - dev->sdr_adc_freq = 300000; - dev->sdr_fm_freq = 50000000; - dev->sdr_pixelformat = V4L2_SDR_FMT_CU8; - dev->sdr_buffersize = SDR_CAP_SAMPLES_PER_BUF * 2; + dev->sdr_cap_adc_freq = 300000; + dev->sdr_cap_fm_freq = 50000000; + dev->sdr_cap_pixelformat = V4L2_SDR_FMT_CU8; + dev->sdr_out_dac_freq = 300000; + dev->sdr_out_fm_freq = 50000000; + dev->sdr_out_pixelformat = V4L2_SDR_FMT_CU8; dev->edid_max_blocks = dev->edid_blocks = 2; memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid)); @@ -1006,6 +1058,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx); v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx); v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_out); /* initialize overlay */ dev->fb_cap.fmt.width = dev->src_rect.width; @@ -1024,6 +1077,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) INIT_LIST_HEAD(&dev->vbi_cap_active); INIT_LIST_HEAD(&dev->vbi_out_active); INIT_LIST_HEAD(&dev->sdr_cap_active); + INIT_LIST_HEAD(&dev->sdr_out_active); /* start creating the vb2 queues */ if (dev->has_vid_cap) { @@ -1120,6 +1174,24 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) goto unreg_dev; } + if (dev->has_sdr_out) { + /* initialize sdr_out queue */ + q = &dev->vb_sdr_out_q; + q->type = V4L2_BUF_TYPE_SDR_OUTPUT; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_WRITE; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct vivid_buffer); + q->ops = &vivid_sdr_out_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 8; + q->lock = &dev->mutex; + + ret = vb2_queue_init(q); + if (ret) + goto unreg_dev; + } + if (dev->has_fb) { /* Create framebuffer for testing capture/output overlay */ ret = vivid_fb_init(dev); @@ -1242,6 +1314,25 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) video_device_node_name(vfd)); } + if (dev->has_sdr_out) { + vfd = &dev->sdr_out_dev; + strlcpy(vfd->name, "vivid-sdr-out", sizeof(vfd->name)); + vfd->vfl_dir = VFL_DIR_TX; + vfd->fops = &vivid_fops; + vfd->ioctl_ops = &vivid_ioctl_ops; + vfd->release = video_device_release_empty; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = &dev->vb_sdr_out_q; + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_SDR, sdr_out_nr[inst]); + if (ret < 0) + goto unreg_dev; + v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n", + video_device_node_name(vfd)); + } + if (dev->has_radio_rx) { vfd = &dev->radio_rx_dev; strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name)); @@ -1286,6 +1377,7 @@ unreg_dev: video_unregister_device(&dev->radio_tx_dev); video_unregister_device(&dev->radio_rx_dev); video_unregister_device(&dev->sdr_cap_dev); + video_unregister_device(&dev->sdr_out_dev); video_unregister_device(&dev->vbi_out_dev); video_unregister_device(&dev->vbi_cap_dev); video_unregister_device(&dev->vid_out_dev); @@ -1372,6 +1464,11 @@ static int vivid_remove(struct platform_device *pdev) video_device_node_name(&dev->sdr_cap_dev)); video_unregister_device(&dev->sdr_cap_dev); } + if (dev->has_sdr_out) { + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", + video_device_node_name(&dev->sdr_out_dev)); + video_unregister_device(&dev->sdr_out_dev); + } if (dev->has_radio_rx) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(&dev->radio_rx_dev)); diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h index 8c7a5ba87c90..79cb7db1da48 100644 --- a/drivers/media/platform/vivid/vivid-core.h +++ b/drivers/media/platform/vivid/vivid-core.h @@ -66,7 +66,7 @@ #define MAX_TV_FREQ (958U * 16U) /* The number of samples returned in every SDR buffer */ -#define SDR_CAP_SAMPLES_PER_BUF 0x4000 +#define SDR_SAMPLES_PER_BUF 0x4000 /* used by the threads to know when to resync internal counters */ #define JIFFIES_PER_DAY (3600U * 24U * HZ) @@ -155,6 +155,8 @@ struct vivid_dev { struct v4l2_ctrl_handler ctrl_hdl_radio_tx; struct video_device sdr_cap_dev; struct v4l2_ctrl_handler ctrl_hdl_sdr_cap; + struct video_device sdr_out_dev; + struct v4l2_ctrl_handler ctrl_hdl_sdr_out; spinlock_t slock; struct mutex mutex; @@ -164,6 +166,7 @@ struct vivid_dev { u32 vbi_cap_caps; u32 vbi_out_caps; u32 sdr_cap_caps; + u32 sdr_out_caps; u32 radio_rx_caps; u32 radio_tx_caps; @@ -188,6 +191,7 @@ struct vivid_dev { bool has_radio_rx; bool has_radio_tx; bool has_sdr_cap; + bool has_sdr_out; bool has_fb; bool can_loop_video; @@ -449,14 +453,35 @@ struct vivid_dev { /* SDR capture */ struct vb2_queue vb_sdr_cap_q; struct list_head sdr_cap_active; - u32 sdr_pixelformat; /* v4l2 format id */ - unsigned sdr_buffersize; - unsigned sdr_adc_freq; - unsigned sdr_fm_freq; + u32 sdr_cap_pixelformat; /* v4l2 format id */ + unsigned sdr_cap_adc_freq; + unsigned sdr_cap_fm_freq; unsigned sdr_fm_deviation; int sdr_fixp_src_phase; int sdr_fixp_mod_phase; + /* thread for generating SDR stream */ + struct task_struct *kthread_sdr_cap; + unsigned long jiffies_sdr_cap; + u32 sdr_cap_seq_offset; + u32 sdr_cap_seq_count; + bool sdr_cap_seq_resync; + + /* SDR output */ + struct vb2_queue vb_sdr_out_q; + struct list_head sdr_out_active; + u32 sdr_out_pixelformat; /* v4l2 format id */ + unsigned sdr_buffersize; + unsigned sdr_out_dac_freq; + unsigned sdr_out_fm_freq; + + /* thread for SDR output stream */ + struct task_struct *kthread_sdr_out; + unsigned long jiffies_sdr_out; + u32 sdr_out_seq_offset; + u32 sdr_out_seq_count; + bool sdr_out_seq_resync; + bool tstamp_src_is_soe; bool has_crop_cap; bool has_compose_cap; @@ -465,13 +490,6 @@ struct vivid_dev { bool has_compose_out; bool has_scaler_out; - /* thread for generating SDR stream */ - struct task_struct *kthread_sdr_cap; - unsigned long jiffies_sdr_cap; - u32 sdr_cap_seq_offset; - u32 sdr_cap_seq_count; - bool sdr_cap_seq_resync; - /* RDS generator */ struct vivid_rds_gen rds_gen; diff --git a/drivers/media/platform/vivid/vivid-radio-tx.c b/drivers/media/platform/vivid/vivid-radio-tx.c index 8c59d4f53200..2cf423968ca8 100644 --- a/drivers/media/platform/vivid/vivid-radio-tx.c +++ b/drivers/media/platform/vivid/vivid-radio-tx.c @@ -109,7 +109,7 @@ unsigned int vivid_radio_tx_poll(struct file *file, struct poll_table_struct *wa return POLLOUT | POLLWRNORM | v4l2_ctrl_poll(file, wait); } -int vidioc_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a) +int vivid_radio_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a) { struct vivid_dev *dev = video_drvdata(file); @@ -128,7 +128,7 @@ int vidioc_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a) return 0; } -int vidioc_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *a) +int vivid_radio_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *a) { struct vivid_dev *dev = video_drvdata(file); diff --git a/drivers/media/platform/vivid/vivid-radio-tx.h b/drivers/media/platform/vivid/vivid-radio-tx.h index 7f8ff7547119..001b927e77d1 100644 --- a/drivers/media/platform/vivid/vivid-radio-tx.h +++ b/drivers/media/platform/vivid/vivid-radio-tx.h @@ -23,7 +23,7 @@ ssize_t vivid_radio_tx_write(struct file *, const char __user *, size_t, loff_t *); unsigned int vivid_radio_tx_poll(struct file *file, struct poll_table_struct *wait); -int vidioc_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a); -int vidioc_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *a); +int vivid_radio_g_modulator(struct file *file, void *fh, struct v4l2_modulator *a); +int vivid_radio_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *a); #endif diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c index 082c401764ce..b6dedd257c2b 100644 --- a/drivers/media/platform/vivid/vivid-sdr-cap.c +++ b/drivers/media/platform/vivid/vivid-sdr-cap.c @@ -33,30 +33,12 @@ #include "vivid-core.h" #include "vivid-ctrls.h" #include "vivid-sdr-cap.h" - -/* stream formats */ -struct vivid_format { - u32 pixelformat; - u32 buffersize; -}; - -/* format descriptions for capture and preview */ -static const struct vivid_format formats[] = { - { - .pixelformat = V4L2_SDR_FMT_CU8, - .buffersize = SDR_CAP_SAMPLES_PER_BUF * 2, - }, { - .pixelformat = V4L2_SDR_FMT_CS8, - .buffersize = SDR_CAP_SAMPLES_PER_BUF * 2, - }, -}; - -static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); +#include "vivid-sdr-common.h" static const struct v4l2_frequency_band bands_adc[] = { { .tuner = 0, - .type = V4L2_TUNER_ADC, + .type = V4L2_TUNER_SDR, .index = 0, .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, .rangelow = 300000, @@ -64,7 +46,7 @@ static const struct v4l2_frequency_band bands_adc[] = { }, { .tuner = 0, - .type = V4L2_TUNER_ADC, + .type = V4L2_TUNER_SDR, .index = 1, .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, .rangelow = 900001, @@ -72,7 +54,7 @@ static const struct v4l2_frequency_band bands_adc[] = { }, { .tuner = 0, - .type = V4L2_TUNER_ADC, + .type = V4L2_TUNER_SDR, .index = 2, .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, .rangelow = 3200000, @@ -163,9 +145,9 @@ static int vivid_thread_sdr_cap(void *data) jiffies_since_start = cur_jiffies - dev->jiffies_sdr_cap; /* Get the number of buffers streamed since the start */ buffers_since_start = - (u64)jiffies_since_start * dev->sdr_adc_freq + - (HZ * SDR_CAP_SAMPLES_PER_BUF) / 2; - do_div(buffers_since_start, HZ * SDR_CAP_SAMPLES_PER_BUF); + (u64)jiffies_since_start * dev->sdr_cap_adc_freq + + (HZ * SDR_SAMPLES_PER_BUF) / 2; + do_div(buffers_since_start, HZ * SDR_SAMPLES_PER_BUF); /* * After more than 0xf0000000 (rounded down to a multiple of @@ -188,20 +170,20 @@ static int vivid_thread_sdr_cap(void *data) * Calculate the number of samples streamed since we started, * not including the current buffer. */ - samples_since_start = buffers_since_start * SDR_CAP_SAMPLES_PER_BUF; + samples_since_start = buffers_since_start * SDR_SAMPLES_PER_BUF; /* And the number of jiffies since we started */ jiffies_since_start = jiffies - dev->jiffies_sdr_cap; /* Increase by the number of samples in one buffer */ - samples_since_start += SDR_CAP_SAMPLES_PER_BUF; + samples_since_start += SDR_SAMPLES_PER_BUF; /* * Calculate when that next buffer is supposed to start * in jiffies since we started streaming. */ next_jiffies_since_start = samples_since_start * HZ + - dev->sdr_adc_freq / 2; - do_div(next_jiffies_since_start, dev->sdr_adc_freq); + dev->sdr_cap_adc_freq / 2; + do_div(next_jiffies_since_start, dev->sdr_cap_adc_freq); /* If it is in the past, then just schedule asap */ if (next_jiffies_since_start < jiffies_since_start) next_jiffies_since_start = jiffies_since_start; @@ -218,7 +200,7 @@ static int sdr_cap_queue_setup(struct vb2_queue *vq, const void *parg, unsigned sizes[], void *alloc_ctxs[]) { /* 2 = max 16-bit sample returned */ - sizes[0] = SDR_CAP_SAMPLES_PER_BUF * 2; + sizes[0] = SDR_SAMPLES_PER_BUF * 2; *nplanes = 1; return 0; } @@ -226,7 +208,7 @@ static int sdr_cap_queue_setup(struct vb2_queue *vq, const void *parg, static int sdr_cap_buf_prepare(struct vb2_buffer *vb) { struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - unsigned size = SDR_CAP_SAMPLES_PER_BUF * 2; + unsigned size = SDR_SAMPLES_PER_BUF * 2; dprintk(dev, 1, "%s\n", __func__); @@ -327,37 +309,18 @@ const struct vb2_ops vivid_sdr_cap_qops = { .wait_finish = vb2_ops_wait_finish, }; -int vivid_sdr_enum_freq_bands(struct file *file, void *fh, - struct v4l2_frequency_band *band) -{ - switch (band->tuner) { - case 0: - if (band->index >= ARRAY_SIZE(bands_adc)) - return -EINVAL; - *band = bands_adc[band->index]; - return 0; - case 1: - if (band->index >= ARRAY_SIZE(bands_fm)) - return -EINVAL; - *band = bands_fm[band->index]; - return 0; - default: - return -EINVAL; - } -} - -int vivid_sdr_g_frequency(struct file *file, void *fh, +int vivid_sdr_cap_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) { struct vivid_dev *dev = video_drvdata(file); switch (vf->tuner) { case 0: - vf->frequency = dev->sdr_adc_freq; - vf->type = V4L2_TUNER_ADC; + vf->frequency = dev->sdr_cap_adc_freq; + vf->type = V4L2_TUNER_SDR; return 0; case 1: - vf->frequency = dev->sdr_fm_freq; + vf->frequency = dev->sdr_cap_fm_freq; vf->type = V4L2_TUNER_RF; return 0; default: @@ -365,7 +328,7 @@ int vivid_sdr_g_frequency(struct file *file, void *fh, } } -int vivid_sdr_s_frequency(struct file *file, void *fh, +int vivid_sdr_cap_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf) { struct vivid_dev *dev = video_drvdata(file); @@ -374,7 +337,7 @@ int vivid_sdr_s_frequency(struct file *file, void *fh, switch (vf->tuner) { case 0: - if (vf->type != V4L2_TUNER_ADC) + if (vf->type != V4L2_TUNER_SDR) return -EINVAL; if (freq < BAND_ADC_0) band = 0; @@ -388,16 +351,16 @@ int vivid_sdr_s_frequency(struct file *file, void *fh, bands_adc[band].rangehigh); if (vb2_is_streaming(&dev->vb_sdr_cap_q) && - freq != dev->sdr_adc_freq) { + freq != dev->sdr_cap_adc_freq) { /* resync the thread's timings */ dev->sdr_cap_seq_resync = true; } - dev->sdr_adc_freq = freq; + dev->sdr_cap_adc_freq = freq; return 0; case 1: if (vf->type != V4L2_TUNER_RF) return -EINVAL; - dev->sdr_fm_freq = clamp_t(unsigned, freq, + dev->sdr_cap_fm_freq = clamp_t(unsigned, freq, bands_fm[0].rangelow, bands_fm[0].rangehigh); return 0; @@ -411,7 +374,7 @@ int vivid_sdr_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) switch (vt->index) { case 0: strlcpy(vt->name, "ADC", sizeof(vt->name)); - vt->type = V4L2_TUNER_ADC; + vt->type = V4L2_TUNER_SDR; vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; vt->rangelow = bands_adc[0].rangelow; @@ -437,20 +400,14 @@ int vivid_sdr_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) return 0; } -int vidioc_enum_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) -{ - if (f->index >= ARRAY_SIZE(formats)) - return -EINVAL; - f->pixelformat = formats[f->index].pixelformat; - return 0; -} - int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f) { struct vivid_dev *dev = video_drvdata(file); + const struct vivid_sdr_fmt *fmt; - f->fmt.sdr.pixelformat = dev->sdr_pixelformat; - f->fmt.sdr.buffersize = dev->sdr_buffersize; + fmt = vivid_sdr_get_format(dev, dev->sdr_cap_pixelformat); + f->fmt.sdr.pixelformat = fmt->pixelformat; + f->fmt.sdr.buffersize = fmt->buffersize; memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); return 0; } @@ -459,40 +416,14 @@ int vidioc_s_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f) { struct vivid_dev *dev = video_drvdata(file); struct vb2_queue *q = &dev->vb_sdr_cap_q; - int i; + const struct vivid_sdr_fmt *fmt; if (vb2_is_busy(q)) return -EBUSY; - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { - dev->sdr_pixelformat = formats[i].pixelformat; - dev->sdr_buffersize = formats[i].buffersize; - f->fmt.sdr.buffersize = formats[i].buffersize; - return 0; - } - } - dev->sdr_pixelformat = formats[0].pixelformat; - dev->sdr_buffersize = formats[0].buffersize; - f->fmt.sdr.pixelformat = formats[0].pixelformat; - f->fmt.sdr.buffersize = formats[0].buffersize; - return 0; -} - -int vidioc_try_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f) -{ - int i; - - memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { - f->fmt.sdr.buffersize = formats[i].buffersize; - return 0; - } - } - f->fmt.sdr.pixelformat = formats[0].pixelformat; - f->fmt.sdr.buffersize = formats[0].buffersize; + vivid_try_fmt_sdr(file, fh, f); + fmt = vivid_sdr_get_format(dev, dev->sdr_cap_pixelformat); + dev->sdr_cap_pixelformat = f->fmt.sdr.pixelformat; return 0; } @@ -515,7 +446,7 @@ void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) /* calculate phase step */ #define BEEP_FREQ 1000 /* 1kHz beep */ src_phase_step = DIV_ROUND_CLOSEST(FIXP_2PI * BEEP_FREQ, - dev->sdr_adc_freq); + dev->sdr_cap_adc_freq); for (i = 0; i < plane_size; i += 2) { mod_phase_step = fixp_cos32_rad(dev->sdr_fixp_src_phase, @@ -544,7 +475,7 @@ void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf) fixp_i >>= (31 - FIXP_N); fixp_q >>= (31 - FIXP_N); - switch (dev->sdr_pixelformat) { + switch (dev->sdr_cap_pixelformat) { case V4L2_SDR_FMT_CU8: /* convert 'fixp float' to u8 [0, +255] */ /* u8 = X * 127.5 + 127.5; X is float [-1.0, +1.0] */ diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.h b/drivers/media/platform/vivid/vivid-sdr-cap.h index 43014b2733db..ef0bc8f8101f 100644 --- a/drivers/media/platform/vivid/vivid-sdr-cap.h +++ b/drivers/media/platform/vivid/vivid-sdr-cap.h @@ -20,15 +20,12 @@ #ifndef _VIVID_SDR_CAP_H_ #define _VIVID_SDR_CAP_H_ -int vivid_sdr_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band); -int vivid_sdr_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); -int vivid_sdr_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf); +int vivid_sdr_cap_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); +int vivid_sdr_cap_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf); int vivid_sdr_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt); int vivid_sdr_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt); -int vidioc_enum_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f); int vidioc_g_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f); int vidioc_s_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f); -int vidioc_try_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f); void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf); extern const struct vb2_ops vivid_sdr_cap_qops; diff --git a/drivers/media/platform/vivid/vivid-sdr-common.c b/drivers/media/platform/vivid/vivid-sdr-common.c new file mode 100644 index 000000000000..7fbffa55d216 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-sdr-common.c @@ -0,0 +1,146 @@ +/* + * vivid-sdr-common.c - software defined radio support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/math64.h> +#include <linux/videodev2.h> +#include <linux/v4l2-dv-timings.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/v4l2-dv-timings.h> +#include <linux/fixp-arith.h> + +#include "vivid-core.h" +#include "vivid-ctrls.h" +#include "vivid-sdr-common.h" + +/* format descriptions for capture and preview */ +static const struct vivid_sdr_fmt formats[] = { + { + .pixelformat = V4L2_SDR_FMT_CU8, + .buffersize = SDR_SAMPLES_PER_BUF * 2, + }, { + .pixelformat = V4L2_SDR_FMT_CS8, + .buffersize = SDR_SAMPLES_PER_BUF * 2, + }, +}; + +static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats); + +static const struct v4l2_frequency_band bands_adc[] = { + { + .tuner = 0, + .type = V4L2_TUNER_SDR, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 300000, + .rangehigh = 300000, + }, + { + .tuner = 0, + .type = V4L2_TUNER_SDR, + .index = 1, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 900001, + .rangehigh = 2800000, + }, + { + .tuner = 0, + .type = V4L2_TUNER_SDR, + .index = 2, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 3200000, + .rangehigh = 3200000, + }, +}; + +/* ADC band midpoints */ +#define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2) +#define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2) + +static const struct v4l2_frequency_band bands_fm[] = { + { + .tuner = 1, + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 50000000, + .rangehigh = 2000000000, + }, +}; + +const struct vivid_sdr_fmt *vivid_sdr_get_format(struct vivid_dev *dev, u32 pixelformat) +{ + const struct vivid_sdr_fmt *fmt; + unsigned k; + + for (k = 0; k < ARRAY_SIZE(formats); k++) { + fmt = &formats[k]; + if (fmt->pixelformat == pixelformat) + return fmt; + } + + return NULL; +} + +int vivid_sdr_enum_freq_bands(struct file *file, void *fh, + struct v4l2_frequency_band *band) +{ + switch (band->tuner) { + case 0: + if (band->index >= ARRAY_SIZE(bands_adc)) + return -EINVAL; + *band = bands_adc[band->index]; + return 0; + case 1: + if (band->index >= ARRAY_SIZE(bands_fm)) + return -EINVAL; + *band = bands_fm[band->index]; + return 0; + default: + return -EINVAL; + } +} + +int vivid_enum_fmt_sdr(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(formats)) + return -EINVAL; + f->pixelformat = formats[f->index].pixelformat; + return 0; +} + +int vivid_try_fmt_sdr(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + const struct vivid_sdr_fmt *fmt; + int i; + + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + fmt = vivid_sdr_get_format(dev, f->fmt.sdr.pixelformat); + if (fmt == NULL) + fmt = &formats[0]; + f->fmt.sdr.pixelformat = fmt->pixelformat; + f->fmt.sdr.buffersize = fmt->buffersize; + return 0; +} diff --git a/drivers/media/platform/vivid/vivid-sdr-common.h b/drivers/media/platform/vivid/vivid-sdr-common.h new file mode 100644 index 000000000000..b9b27fafa53f --- /dev/null +++ b/drivers/media/platform/vivid/vivid-sdr-common.h @@ -0,0 +1,35 @@ +/* + * vivid-sdr-common.h - software defined radio support functions. + * + * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_SDR_COMMON_H_ +#define _VIVID_SDR_COMMON_H_ + +/* SDR stream formats */ +struct vivid_sdr_fmt { + u32 pixelformat; + u32 buffersize; +}; + +const struct vivid_sdr_fmt *vivid_sdr_get_format(struct vivid_dev *dev, u32 pixelformat); + +int vivid_sdr_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band); +int vivid_enum_fmt_sdr(struct file *file, void *fh, struct v4l2_fmtdesc *f); +int vivid_try_fmt_sdr(struct file *file, void *fh, struct v4l2_format *f); + +#endif diff --git a/drivers/media/platform/vivid/vivid-sdr-out.c b/drivers/media/platform/vivid/vivid-sdr-out.c new file mode 100644 index 000000000000..2090b5eeb29f --- /dev/null +++ b/drivers/media/platform/vivid/vivid-sdr-out.c @@ -0,0 +1,499 @@ +/* + * vivid-sdr-out.c - software defined radio support functions. + * + * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/math64.h> +#include <linux/videodev2.h> +#include <linux/v4l2-dv-timings.h> +#include <media/v4l2-common.h> +#include <media/v4l2-event.h> +#include <media/v4l2-dv-timings.h> +#include <linux/fixp-arith.h> + +#include "vivid-core.h" +#include "vivid-ctrls.h" +#include "vivid-sdr-out.h" +#include "vivid-sdr-common.h" + +static const struct v4l2_frequency_band bands_dac[] = { + { + .tuner = 0, + .type = V4L2_TUNER_SDR, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 300000, + .rangehigh = 300000, + }, + { + .tuner = 0, + .type = V4L2_TUNER_SDR, + .index = 1, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 900001, + .rangehigh = 2800000, + }, + { + .tuner = 0, + .type = V4L2_TUNER_SDR, + .index = 2, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 3200000, + .rangehigh = 3200000, + }, +}; + +/* DAC band midpoints */ +#define BAND_DAC_0 ((bands_dac[0].rangehigh + bands_dac[1].rangelow) / 2) +#define BAND_DAC_1 ((bands_dac[1].rangehigh + bands_dac[2].rangelow) / 2) + +static const struct v4l2_frequency_band bands_fm[] = { + { + .tuner = 1, + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 50000000, + .rangehigh = 2000000000, + }, +}; + +static void vivid_thread_sdr_out_tick(struct vivid_dev *dev) +{ + struct vivid_buffer *sdr_out_buf = NULL; + + dprintk(dev, 1, "SDR Capture Thread Tick\n"); + + /* Drop a certain percentage of buffers. */ + if (dev->perc_dropped_buffers && + prandom_u32_max(100) < dev->perc_dropped_buffers) + return; + + spin_lock(&dev->slock); + if (!list_empty(&dev->sdr_out_active)) { + sdr_out_buf = list_entry(dev->sdr_out_active.next, + struct vivid_buffer, list); + list_del(&sdr_out_buf->list); + } + spin_unlock(&dev->slock); + + if (sdr_out_buf) { + sdr_out_buf->vb.sequence = dev->sdr_out_seq_count; + vivid_sdr_out_process(dev, sdr_out_buf); + v4l2_get_timestamp(&sdr_out_buf->vb.timestamp); + sdr_out_buf->vb.timestamp.tv_sec += dev->time_wrap_offset; + vb2_buffer_done(&sdr_out_buf->vb.vb2_buf, dev->dqbuf_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + dev->dqbuf_error = false; + } +} + +static int vivid_thread_sdr_out(void *data) +{ + struct vivid_dev *dev = data; + u64 samples_since_start; + u64 buffers_since_start; + u64 next_jiffies_since_start; + unsigned long jiffies_since_start; + unsigned long cur_jiffies; + unsigned wait_jiffies; + + dprintk(dev, 1, "SDR Capture Thread Start\n"); + + set_freezable(); + + /* Resets frame counters */ + dev->sdr_out_seq_offset = 0; + if (dev->seq_wrap) + dev->sdr_out_seq_offset = 0xffffff80U; + dev->jiffies_sdr_out = jiffies; + dev->sdr_out_seq_resync = false; + + for (;;) { + try_to_freeze(); + if (kthread_should_stop()) + break; + + mutex_lock(&dev->mutex); + cur_jiffies = jiffies; + if (dev->sdr_out_seq_resync) { + dev->jiffies_sdr_out = cur_jiffies; + dev->sdr_out_seq_offset = dev->sdr_out_seq_count + 1; + dev->sdr_out_seq_count = 0; + dev->sdr_out_seq_resync = false; + } + /* Calculate the number of jiffies since we started streaming */ + jiffies_since_start = cur_jiffies - dev->jiffies_sdr_out; + /* Get the number of buffers streamed since the start */ + buffers_since_start = + (u64)jiffies_since_start * dev->sdr_out_dac_freq + + (HZ * SDR_SAMPLES_PER_BUF) / 2; + do_div(buffers_since_start, HZ * SDR_SAMPLES_PER_BUF); + + /* + * After more than 0xf0000000 (rounded down to a multiple of + * 'jiffies-per-day' to ease jiffies_to_msecs calculation) + * jiffies have passed since we started streaming reset the + * counters and keep track of the sequence offset. + */ + if (jiffies_since_start > JIFFIES_RESYNC) { + dev->jiffies_sdr_out = cur_jiffies; + dev->sdr_out_seq_offset = buffers_since_start; + buffers_since_start = 0; + } + dev->sdr_out_seq_count = + buffers_since_start + dev->sdr_out_seq_offset; + + vivid_thread_sdr_out_tick(dev); + mutex_unlock(&dev->mutex); + + /* + * Calculate the number of samples streamed since we started, + * not including the current buffer. + */ + samples_since_start = buffers_since_start * SDR_SAMPLES_PER_BUF; + + /* And the number of jiffies since we started */ + jiffies_since_start = jiffies - dev->jiffies_sdr_out; + + /* Increase by the number of samples in one buffer */ + samples_since_start += SDR_SAMPLES_PER_BUF; + /* + * Calculate when that next buffer is supposed to start + * in jiffies since we started streaming. + */ + next_jiffies_since_start = samples_since_start * HZ + + dev->sdr_out_dac_freq / 2; + do_div(next_jiffies_since_start, dev->sdr_out_dac_freq); + /* If it is in the past, then just schedule asap */ + if (next_jiffies_since_start < jiffies_since_start) + next_jiffies_since_start = jiffies_since_start; + + wait_jiffies = next_jiffies_since_start - jiffies_since_start; + schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1); + } + dprintk(dev, 1, "SDR Capture Thread End\n"); + return 0; +} + +static int sdr_out_queue_setup(struct vb2_queue *vq, const void *parg, + unsigned *nbuffers, unsigned *nplanes, + unsigned sizes[], void *alloc_ctxs[]) +{ + /* 2 = max 16-bit sample returned */ + sizes[0] = SDR_SAMPLES_PER_BUF * 2; + *nplanes = 1; + return 0; +} + +static int sdr_out_buf_prepare(struct vb2_buffer *vb) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + unsigned size = SDR_SAMPLES_PER_BUF * 2; + + dprintk(dev, 1, "%s\n", __func__); + + if (dev->buf_prepare_error) { + /* + * Error injection: test what happens if buf_prepare() returns + * an error. + */ + dev->buf_prepare_error = false; + return -EINVAL; + } + if (vb2_plane_size(vb, 0) < size) { + dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static void sdr_out_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); + + dprintk(dev, 1, "%s\n", __func__); + + spin_lock(&dev->slock); + list_add_tail(&buf->list, &dev->sdr_out_active); + spin_unlock(&dev->slock); +} + +static int sdr_out_start_streaming(struct vb2_queue *vq, unsigned count) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + int err = 0; + + dprintk(dev, 1, "%s\n", __func__); + dev->sdr_out_seq_count = 0; + if (dev->start_streaming_error) { + dev->start_streaming_error = false; + err = -EINVAL; + } else if (dev->kthread_sdr_out == NULL) { + dev->kthread_sdr_out = kthread_run(vivid_thread_sdr_out, dev, + "%s-sdr-out", dev->v4l2_dev.name); + + if (IS_ERR(dev->kthread_sdr_out)) { + v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); + err = PTR_ERR(dev->kthread_sdr_out); + dev->kthread_sdr_out = NULL; + } + } + if (err) { + struct vivid_buffer *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &dev->sdr_out_active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); + } + } + return err; +} + +/* abort streaming and wait for last buffer */ +static void sdr_out_stop_streaming(struct vb2_queue *vq) +{ + struct vivid_dev *dev = vb2_get_drv_priv(vq); + + if (dev->kthread_sdr_out == NULL) + return; + + while (!list_empty(&dev->sdr_out_active)) { + struct vivid_buffer *buf; + + buf = list_entry(dev->sdr_out_active.next, + struct vivid_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + /* shutdown control thread */ + mutex_unlock(&dev->mutex); + kthread_stop(dev->kthread_sdr_out); + dev->kthread_sdr_out = NULL; + mutex_lock(&dev->mutex); +} + +const struct vb2_ops vivid_sdr_out_qops = { + .queue_setup = sdr_out_queue_setup, + .buf_prepare = sdr_out_buf_prepare, + .buf_queue = sdr_out_buf_queue, + .start_streaming = sdr_out_start_streaming, + .stop_streaming = sdr_out_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +int vivid_sdr_out_g_frequency(struct file *file, void *fh, + struct v4l2_frequency *vf) +{ + struct vivid_dev *dev = video_drvdata(file); + + switch (vf->tuner) { + case 0: + vf->frequency = dev->sdr_out_dac_freq; + vf->type = V4L2_TUNER_SDR; + return 0; + case 1: + vf->frequency = dev->sdr_out_fm_freq; + vf->type = V4L2_TUNER_RF; + return 0; + default: + return -EINVAL; + } +} + +int vivid_sdr_out_s_frequency(struct file *file, void *fh, + const struct v4l2_frequency *vf) +{ + struct vivid_dev *dev = video_drvdata(file); + unsigned freq = vf->frequency; + unsigned band; + + switch (vf->tuner) { + case 0: + if (vf->type != V4L2_TUNER_SDR) + return -EINVAL; + if (freq < BAND_DAC_0) + band = 0; + else if (freq < BAND_DAC_1) + band = 1; + else + band = 2; + + freq = clamp_t(unsigned, freq, + bands_dac[band].rangelow, + bands_dac[band].rangehigh); + + if (vb2_is_streaming(&dev->vb_sdr_out_q) && + freq != dev->sdr_out_dac_freq) { + /* resync the thread's timings */ + dev->sdr_out_seq_resync = true; + } + dev->sdr_out_dac_freq = freq; + return 0; + case 1: + if (vf->type != V4L2_TUNER_RF) + return -EINVAL; + dev->sdr_out_fm_freq = clamp_t(unsigned, freq, + bands_fm[0].rangelow, + bands_fm[0].rangehigh); + return 0; + default: + return -EINVAL; + } +} + +int vivid_sdr_g_modulator(struct file *file, void *fh, struct v4l2_modulator *mt) +{ + switch (mt->index) { + case 0: + strlcpy(mt->name, "DAC", sizeof(mt->name)); + mt->type = V4L2_TUNER_SDR; + mt->capability = + V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + mt->rangelow = bands_dac[0].rangelow; + mt->rangehigh = bands_dac[2].rangehigh; + return 0; + case 1: + strlcpy(mt->name, "RF", sizeof(mt->name)); + mt->type = V4L2_TUNER_RF; + mt->capability = + V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + mt->rangelow = bands_fm[0].rangelow; + mt->rangehigh = bands_fm[0].rangehigh; + return 0; + default: + return -EINVAL; + } +} + +int vivid_sdr_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *mt) +{ + if (mt->index > 1) + return -EINVAL; + return 0; +} + +int vidioc_g_fmt_sdr_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + const struct vivid_sdr_fmt *fmt; + + fmt = vivid_sdr_get_format(dev, dev->sdr_out_pixelformat); + f->fmt.sdr.pixelformat = fmt->pixelformat; + f->fmt.sdr.buffersize = fmt->buffersize; + memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); + return 0; +} + +int vidioc_s_fmt_sdr_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct vivid_dev *dev = video_drvdata(file); + struct vb2_queue *q = &dev->vb_sdr_out_q; + const struct vivid_sdr_fmt *fmt; + + if (vb2_is_busy(q)) + return -EBUSY; + + vivid_try_fmt_sdr(file, fh, f); + fmt = vivid_sdr_get_format(dev, dev->sdr_cap_pixelformat); + dev->sdr_out_pixelformat = fmt->pixelformat; + return 0; +} + +#define FIXP_N (15) +#define FIXP_FRAC (1 << FIXP_N) +#define FIXP_2PI ((int)(2 * 3.141592653589 * FIXP_FRAC)) +#define M_100000PI (3.14159 * 100000) + +void vivid_sdr_out_process(struct vivid_dev *dev, struct vivid_buffer *buf) +{ + u8 *vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + unsigned long i; + unsigned long plane_size = vb2_plane_size(&buf->vb.vb2_buf, 0); + s64 s64tmp; + s32 src_phase_step; + s32 mod_phase_step; + s32 fixp_i; + s32 fixp_q; + + /* calculate phase step */ + #define BEEP_FREQ 1000 /* 1kHz beep */ + src_phase_step = DIV_ROUND_CLOSEST(FIXP_2PI * BEEP_FREQ, + dev->sdr_out_dac_freq); + + for (i = 0; i < plane_size; i += 2) { + mod_phase_step = fixp_cos32_rad(dev->sdr_fixp_src_phase, + FIXP_2PI) >> (31 - FIXP_N); + + dev->sdr_fixp_src_phase += src_phase_step; + s64tmp = (s64) mod_phase_step * dev->sdr_fm_deviation; + dev->sdr_fixp_mod_phase += div_s64(s64tmp, M_100000PI); + + /* + * Transfer phase angle to [0, 2xPI] in order to avoid variable + * overflow and make it suitable for cosine implementation + * used, which does not support negative angles. + */ + dev->sdr_fixp_src_phase %= FIXP_2PI; + dev->sdr_fixp_mod_phase %= FIXP_2PI; + + if (dev->sdr_fixp_mod_phase < 0) + dev->sdr_fixp_mod_phase += FIXP_2PI; + + fixp_i = fixp_cos32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI); + fixp_q = fixp_sin32_rad(dev->sdr_fixp_mod_phase, FIXP_2PI); + + /* Normalize fraction values represented with 32 bit precision + * to fixed point representation with FIXP_N bits */ + fixp_i >>= (31 - FIXP_N); + fixp_q >>= (31 - FIXP_N); + + switch (dev->sdr_out_pixelformat) { + case V4L2_SDR_FMT_CU8: + /* convert 'fixp float' to u8 [0, +255] */ + /* u8 = X * 127.5 + 127.5; X is float [-1.0, +1.0] */ + fixp_i = fixp_i * 1275 + FIXP_FRAC * 1275; + fixp_q = fixp_q * 1275 + FIXP_FRAC * 1275; + *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10); + *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10); + break; + case V4L2_SDR_FMT_CS8: + /* convert 'fixp float' to s8 [-128, +127] */ + /* s8 = X * 127.5 - 0.5; X is float [-1.0, +1.0] */ + fixp_i = fixp_i * 1275 - FIXP_FRAC * 5; + fixp_q = fixp_q * 1275 - FIXP_FRAC * 5; + *vbuf++ = DIV_ROUND_CLOSEST(fixp_i, FIXP_FRAC * 10); + *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10); + break; + default: + break; + } + } +} diff --git a/drivers/media/platform/vivid/vivid-sdr-out.h b/drivers/media/platform/vivid/vivid-sdr-out.h new file mode 100644 index 000000000000..ae973d86cd70 --- /dev/null +++ b/drivers/media/platform/vivid/vivid-sdr-out.h @@ -0,0 +1,33 @@ +/* + * vivid-sdr-out.h - software defined radio support functions. + * + * Copyright 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VIVID_SDR_OUT_H_ +#define _VIVID_SDR_OUT_H_ + +int vivid_sdr_out_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); +int vivid_sdr_out_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf); +int vivid_sdr_g_modulator(struct file *file, void *fh, struct v4l2_modulator *mt); +int vivid_sdr_s_modulator(struct file *file, void *fh, const struct v4l2_modulator *mt); +int vidioc_g_fmt_sdr_out(struct file *file, void *fh, struct v4l2_format *f); +int vidioc_s_fmt_sdr_out(struct file *file, void *fh, struct v4l2_format *f); +void vivid_sdr_out_process(struct vivid_dev *dev, struct vivid_buffer *buf); + +extern const struct vb2_ops vivid_sdr_out_qops; + +#endif |