[media] vivid: sdr cap: few enhancements
[firefly-linux-kernel-4.4.55.git] / drivers / media / platform / vivid / vivid-sdr-cap.c
index d2f2188a0efe78ac7f13bab293b00dc53d6084eb..d0c26577c06ea29139942211ea5863982fe1ff43 100644 (file)
@@ -22,6 +22,7 @@
 #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>
@@ -40,7 +41,7 @@ struct vivid_format {
 };
 
 /* format descriptions for capture and preview */
-static struct vivid_format formats[] = {
+static const struct vivid_format formats[] = {
        {
                .pixelformat    = V4L2_SDR_FMT_CU8,
                .buffersize     = SDR_CAP_SAMPLES_PER_BUF * 2,
@@ -488,47 +489,42 @@ int vidioc_try_fmt_sdr_cap(struct file *file, void *fh, struct v4l2_format *f)
 #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_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf)
 {
        u8 *vbuf = vb2_plane_vaddr(&buf->vb, 0);
        unsigned long i;
        unsigned long plane_size = vb2_plane_size(&buf->vb, 0);
+       s64 s64tmp;
        s32 src_phase_step;
        s32 mod_phase_step;
        s32 fixp_i;
        s32 fixp_q;
 
-       /*
-        * TODO: Generated beep tone goes very crackly when sample rate is
-        * increased to ~1Msps or more. That is because of huge rounding error
-        * of phase angle caused by used cosine implementation.
-        */
-
        /* 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_adc_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;
-               dev->sdr_fixp_mod_phase += mod_phase_step / 4;
+               s64tmp = (s64) mod_phase_step * dev->sdr_fm_deviation;
+               dev->sdr_fixp_mod_phase += div_s64(s64tmp, M_100000PI);
 
                /*
-                * Transfer phases to [0 / 2xPI] in order to avoid variable
+                * 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.
                 */
-               while (dev->sdr_fixp_mod_phase < FIXP_2PI)
-                       dev->sdr_fixp_mod_phase += FIXP_2PI;
-               while (dev->sdr_fixp_mod_phase > FIXP_2PI)
-                       dev->sdr_fixp_mod_phase -= FIXP_2PI;
+               dev->sdr_fixp_src_phase %= FIXP_2PI;
+               dev->sdr_fixp_mod_phase %= FIXP_2PI;
 
-               while (dev->sdr_fixp_src_phase > FIXP_2PI)
-                       dev->sdr_fixp_src_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);
@@ -540,7 +536,7 @@ void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf)
 
                switch (dev->sdr_pixelformat) {
                case V4L2_SDR_FMT_CU8:
-                       /* convert 'fixp float' to u8 */
+                       /* 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;
@@ -548,9 +544,10 @@ void vivid_sdr_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf)
                        *vbuf++ = DIV_ROUND_CLOSEST(fixp_q, FIXP_FRAC * 10);
                        break;
                case V4L2_SDR_FMT_CS8:
-                       /* convert 'fixp float' to s8 */
-                       fixp_i = fixp_i * 1275;
-                       fixp_q = fixp_q * 1275;
+                       /* 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;