[PATCH v2 1/3] staging: xm2mvscale: Driver support for Xilinx M2M Video Scaler
Rohit Athavale
rohit.athavale at xilinx.com
Wed Feb 21 22:43:14 UTC 2018
This commit adds driver support for the pre-release Xilinx M2M Video
Scaler IP. There are three parts to this driver :
- The Hardware/IP layer that reads and writes register of the IP
contained in the scaler_hw_xm2m.c
- The set of ioctls that applications would need to know contained
in ioctl_xm2mvsc.h
- The char driver that consumes the IP layer in xm2m_vscale.c
Signed-off-by: Rohit Athavale <rohit.athavale at xilinx.com>
---
drivers/staging/Kconfig | 2 +
drivers/staging/Makefile | 1 +
drivers/staging/xm2mvscale/Kconfig | 11 +
drivers/staging/xm2mvscale/Makefile | 3 +
drivers/staging/xm2mvscale/ioctl_xm2mvsc.h | 134 ++++
drivers/staging/xm2mvscale/scaler_hw_xm2m.c | 945 ++++++++++++++++++++++++++
drivers/staging/xm2mvscale/scaler_hw_xm2m.h | 152 +++++
drivers/staging/xm2mvscale/xm2m_vscale.c | 768 +++++++++++++++++++++
drivers/staging/xm2mvscale/xvm2mvsc_hw_regs.h | 204 ++++++
9 files changed, 2220 insertions(+)
create mode 100644 drivers/staging/xm2mvscale/Kconfig
create mode 100644 drivers/staging/xm2mvscale/Makefile
create mode 100644 drivers/staging/xm2mvscale/ioctl_xm2mvsc.h
create mode 100644 drivers/staging/xm2mvscale/scaler_hw_xm2m.c
create mode 100644 drivers/staging/xm2mvscale/scaler_hw_xm2m.h
create mode 100644 drivers/staging/xm2mvscale/xm2m_vscale.c
create mode 100644 drivers/staging/xm2mvscale/xvm2mvsc_hw_regs.h
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index e95ab68..3ee3a3e 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -122,4 +122,6 @@ source "drivers/staging/vboxvideo/Kconfig"
source "drivers/staging/pi433/Kconfig"
+source "drivers/staging/xm2mvscale/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index af8cd6a..29ce417 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -52,3 +52,4 @@ obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/
obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
obj-$(CONFIG_PI433) += pi433/
+obj-$(CONFIG_XILINX_M2M_VSC) += xm2mvscale/
diff --git a/drivers/staging/xm2mvscale/Kconfig b/drivers/staging/xm2mvscale/Kconfig
new file mode 100644
index 0000000..949a8e4
--- /dev/null
+++ b/drivers/staging/xm2mvscale/Kconfig
@@ -0,0 +1,11 @@
+config XILINX_M2M_VSC
+ tristate "Xilinx M2M Video Scaler"
+ depends on ARCH_ZYNQMP && OF
+ ---help---
+ This driver is developed for Xilinx M2M Video Scaler IP,
+ designed to allow passage of frame buffers from the source
+ kernel sub-system, apply video scaling and forward to the
+ sink kernel sub-system.
+
+ To compile this driver as a module, choose M here.
+ If unsure, choose N.
diff --git a/drivers/staging/xm2mvscale/Makefile b/drivers/staging/xm2mvscale/Makefile
new file mode 100644
index 0000000..ec777e1
--- /dev/null
+++ b/drivers/staging/xm2mvscale/Makefile
@@ -0,0 +1,3 @@
+xm2m_vscale_drv-y := scaler_hw_xm2m.o
+xm2m_vscale_drv-y += xm2m_vscale.o
+obj-$(CONFIG_XILINX_M2M_VSC) += xm2m_vscale_drv.o
diff --git a/drivers/staging/xm2mvscale/ioctl_xm2mvsc.h b/drivers/staging/xm2mvscale/ioctl_xm2mvsc.h
new file mode 100644
index 0000000..03bd7ab
--- /dev/null
+++ b/drivers/staging/xm2mvscale/ioctl_xm2mvsc.h
@@ -0,0 +1,134 @@
+/*
+ * Xilinx Memory-to-Memory Video Scaler IP
+ *
+ * Copyright (C) 2018 Xilinx, Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+ */
+#ifndef __IOCTL_XM2MVSC_H__
+#define __IOCTL_XM2MVSC_H__
+
+/* Xilinx Video Specific Color/Pixel Formats */
+enum xm2mvsc_pix_fmt {
+ XILINX_FRMBUF_FMT_RGBX8 = 10,
+ XILINX_FRMBUF_FMT_YUVX8,
+ XILINX_FRMBUF_FMT_YUYV8,
+ XILINX_FRMBUF_FMT_RGBA8,
+ XILINX_FRMBUF_FMT_YUVA8,
+ XILINX_FRMBUF_FMT_RGBX10,
+ XILINX_FRMBUF_FMT_YUVX10,
+ /* RGB565 takes the value of 17 */
+ XILINX_FRMBUF_FMT_Y_UV8 = 18,
+ XILINX_FRMBUF_FMT_Y_UV8_420,
+ XILINX_FRMBUF_FMT_RGB8,
+ XILINX_FRMBUF_FMT_YUV8,
+ XILINX_FRMBUF_FMT_Y_UV10,
+ XILINX_FRMBUF_FMT_Y_UV10_420,
+ XILINX_FRMBUF_FMT_Y8,
+ XILINX_FRMBUF_FMT_Y10,
+ XILINX_FRMBUF_FMT_BGRA8,
+ XILINX_FRMBUF_FMT_BGRX8,
+ XILINX_FRMBUF_FMT_UYVY8,
+ XILINX_FRMBUF_FMT_BGR8,
+};
+
+/* struct xm2mvsc_qdata - Struct to enqueue a descriptor
+ * @srcbuf_ht: Height of source buffer
+ * @srcbuf_wt: Width of source buffer
+ * @srcbuf_bpp: Bytes per pixel of source buffer
+ * @srcbuf_cft: Color/Pixel format of source buffer
+ * @srcbuf_size: Size of the source buffer requested
+ * @srcbuf_mmap: Identify if srcbuf is mmap'ed
+ * @srcbuf_stride: Stride of the source buffer
+ * @dstbuf_ht: Height of destination buffer
+ * @dstbuf_wt: Width of destination buffer
+ * @dstbuf_bpp: Bytes per pixel of destination buffer
+ * @dstbuf_cft: Color/Pixel format of source buffer
+ * @dstbuf_size: Size of the source buffer requested
+ * @dstbuf_mmap: Identify if srcbuf is mmap'ed
+ * @dstbuf_stride: Stride of the source buffer
+ * @dstbuf_cft: Color Format of destination buffer
+ * @desc_id: Keep a track of the descriptors
+ */
+struct xm2mvsc_qdata {
+ /* Source information */
+ u32 srcbuf_ht;
+ u32 srcbuf_wt;
+ u32 srcbuf_bpp;
+ enum xm2mvsc_pix_fmt srcbuf_cft;
+ size_t srcbuf_size;
+ /* srcbuf_mmap : For use by the library, do not touch */
+ bool srcbuf_mmap;
+ u16 srcbuf_stride;
+ /* Destination information */
+ u32 dstbuf_ht;
+ u32 dstbuf_wt;
+ u32 dstbuf_bpp;
+ enum xm2mvsc_pix_fmt dstbuf_cft;
+ size_t dstbuf_size;
+ /* dstbuf_mmap : For use by the library, do not touch */
+ bool dstbuf_mmap;
+ u16 dstbuf_stride;
+ u32 desc_id;
+};
+
+/**
+ * struct xm2mvsc_dqdata - Struct to dequeue a completed descriptor
+ * @desc_id: Descriptor ID that needs to be dequeued
+ */
+struct xm2mvsc_dqdata {
+ u32 desc_id;
+};
+
+/**
+ * struct xm2mvsc_batch - Struct to specify the batch size
+ * @batch_size: Number of channels the scaler should operate per scaling op
+ */
+struct xm2mvsc_batch {
+ u16 batch_size;
+};
+
+/* XM2MVSCALE IOCTL LIST */
+#define XM2MVSC_MAGIC 'X'
+
+/*
+ * DOC: XM2MVSC_ENQUEUE
+ * Enqueue a descriptor that describes the scaling operation for a channel.
+ * Returns the descriptor ID
+ */
+#define XM2MVSC_ENQUEUE _IOWR(XM2MVSC_MAGIC, 1, struct xm2mvsc_qdata *)
+
+/*
+ * DOC: XM2MVSC_START
+ * Start the M2M Scaler IP. Driver will operate on descriptors in the
+ * pending list.
+ */
+#define XM2MVSC_START _IO(XM2MVSC_MAGIC, 2)
+
+/*
+ * DOC: XM2MVSC_DEQUEUE
+ * Dequeue a descriptor by providing the driver with information about the
+ * descriptor that needs to be dequeued.
+ */
+#define XM2MVSC_DEQUEUE _IOW(XM2MVSC_MAGIC, 3, struct xm2mvsc_dqdata *)
+
+/*
+ * DOC: XM2MVSC_STOP
+ * Stop the M2M Scaler IP. Clear driver state and reset the IP.
+ */
+#define XM2MVSC_STOP _IO(XM2MVSC_MAGIC, 4)
+
+/*
+ * DOC: XM2MVSC_FREE
+ * Free a descriptor after being dequeued via XM2MVSC_DEQUEUE ioctl.
+ */
+#define XM2MVSC_FREE _IOW(XM2MVSC_MAGIC, 5, struct xm2mvsc_dqdata *)
+
+/*
+ * DOC: XM2MVSC_BATCH_SIZE
+ * Set the batch size that the M2M Scaler IP should use when programming the
+ * scaler. Driver may reject the incoming batch size.
+ */
+#define XM2MVSC_BATCH_SIZE _IOW(XM2MVSC_MAGIC, 6, struct xm2mvsc_batch *)
+
+#endif /* __IOCTL_XM2MVSC_H__ */
diff --git a/drivers/staging/xm2mvscale/scaler_hw_xm2m.c b/drivers/staging/xm2mvscale/scaler_hw_xm2m.c
new file mode 100644
index 0000000..a6e35ae
--- /dev/null
+++ b/drivers/staging/xm2mvscale/scaler_hw_xm2m.c
@@ -0,0 +1,945 @@
+/*
+ * Xilinx Memory-to-Memory Video Scaler IP
+ *
+ * Copyright (C) 2018 Xilinx, Inc. All rights reserved.
+ *
+ * Description:
+ * This driver is developed for the Xilinx M2M Video Scaler IP. It allows
+ * userspace to access the IP registers and takes care of interrupt handling
+ * and framebuffer programming within the driver.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <linux/bitops.h>
+
+#include "xvm2mvsc_hw_regs.h"
+#include "scaler_hw_xm2m.h"
+
+/* H-scaler coefficients for 6, 8, 10 and 12 tap filters */
+static const u16
+xhsc_coeff_taps6[XSCALER_MAX_PHASES][XV_SCALER_TAPS_6] = {
+ { -132, 236, 3824, 236, -132, 64, },
+ { -116, 184, 3816, 292, -144, 64, },
+ { -100, 132, 3812, 348, -160, 64, },
+ { -88, 84, 3808, 404, -176, 64, },
+ { -72, 36, 3796, 464, -192, 64, },
+ { -60, -8, 3780, 524, -208, 68, },
+ { -48, -52, 3768, 588, -228, 68, },
+ { -32, -96, 3748, 652, -244, 68, },
+ { -20, -136, 3724, 716, -260, 72, },
+ { -8, -172, 3696, 784, -276, 72, },
+ { 0, -208, 3676, 848, -292, 72, },
+ { 12, -244, 3640, 920, -308, 76, },
+ { 20, -276, 3612, 988, -324, 76, },
+ { 32, -304, 3568, 1060, -340, 80, },
+ { 40, -332, 3532, 1132, -356, 80, },
+ { 48, -360, 3492, 1204, -372, 84, },
+ { 56, -384, 3448, 1276, -388, 88, },
+ { 64, -408, 3404, 1352, -404, 88, },
+ { 72, -428, 3348, 1428, -416, 92, },
+ { 76, -448, 3308, 1500, -432, 92, },
+ { 84, -464, 3248, 1576, -444, 96, },
+ { 88, -480, 3200, 1652, -460, 96, },
+ { 92, -492, 3140, 1728, -472, 100, },
+ { 96, -504, 3080, 1804, -484, 104, },
+ { 100, -516, 3020, 1880, -492, 104, },
+ { 104, -524, 2956, 1960, -504, 104, },
+ { 104, -532, 2892, 2036, -512, 108, },
+ { 108, -540, 2832, 2108, -520, 108, },
+ { 108, -544, 2764, 2184, -528, 112, },
+ { 112, -544, 2688, 2260, -532, 112, },
+ { 112, -548, 2624, 2336, -540, 112, },
+ { 112, -548, 2556, 2408, -544, 112, },
+ { 112, -544, 2480, 2480, -544, 112, },
+ { 112, -544, 2408, 2556, -548, 112, },
+ { 112, -540, 2336, 2624, -548, 112, },
+ { 112, -532, 2260, 2688, -544, 112, },
+ { 112, -528, 2184, 2764, -544, 108, },
+ { 108, -520, 2108, 2832, -540, 108, },
+ { 108, -512, 2036, 2892, -532, 104, },
+ { 104, -504, 1960, 2956, -524, 104, },
+ { 104, -492, 1880, 3020, -516, 100, },
+ { 104, -484, 1804, 3080, -504, 96, },
+ { 100, -472, 1728, 3140, -492, 92, },
+ { 96, -460, 1652, 3200, -480, 88, },
+ { 96, -444, 1576, 3248, -464, 84, },
+ { 92, -432, 1500, 3308, -448, 76, },
+ { 92, -416, 1428, 3348, -428, 72, },
+ { 88, -404, 1352, 3404, -408, 64, },
+ { 88, -388, 1276, 3448, -384, 56, },
+ { 84, -372, 1204, 3492, -360, 48, },
+ { 80, -356, 1132, 3532, -332, 40, },
+ { 80, -340, 1060, 3568, -304, 32, },
+ { 76, -324, 988, 3612, -276, 20, },
+ { 76, -308, 920, 3640, -244, 12, },
+ { 72, -292, 848, 3676, -208, 0, },
+ { 72, -276, 784, 3696, -172, -8, },
+ { 72, -260, 716, 3724, -136, -20, },
+ { 68, -244, 652, 3748, -96, -32, },
+ { 68, -228, 588, 3768, -52, -48, },
+ { 68, -208, 524, 3780, -8, -60, },
+ { 64, -192, 464, 3796, 36, -72, },
+ { 64, -176, 404, 3808, 84, -88, },
+ { 64, -160, 348, 3812, 132, -100, },
+ { 64, -144, 292, 3816, 184, -116, }
+};
+
+static const u16
+xhsc_coeff_taps8[XSCALER_MAX_PHASES][XV_SCALER_TAPS_8] = {
+ {-5, 309, 1023, 1445, 1034, 317, -3, -24, },
+ {-6, 300, 1011, 1445, 1045, 326, -1, -24, },
+ {-7, 291, 1000, 1444, 1056, 336, 0, -24, },
+ {-9, 282, 988, 1444, 1067, 345, 2, -24, },
+ {-10, 274, 977, 1443, 1078, 354, 4, -24, },
+ {-11, 266, 965, 1441, 1089, 364, 6, -24, },
+ {-12, 258, 953, 1440, 1100, 373, 8, -24, },
+ {-13, 250, 942, 1438, 1110, 383, 10, -24, },
+ {-14, 242, 930, 1437, 1121, 393, 12, -24, },
+ {-15, 234, 918, 1434, 1131, 403, 14, -24, },
+ {-16, 226, 906, 1432, 1142, 413, 17, -24, },
+ {-17, 219, 894, 1430, 1152, 423, 19, -24, },
+ {-17, 211, 882, 1427, 1162, 433, 22, -24, },
+ {-18, 204, 870, 1424, 1172, 443, 24, -24, },
+ {-19, 197, 858, 1420, 1182, 454, 27, -24, },
+ {-19, 190, 846, 1417, 1191, 464, 30, -24, },
+ {-20, 183, 834, 1413, 1201, 475, 33, -24, },
+ {-20, 176, 822, 1409, 1210, 486, 36, -24, },
+ {-21, 170, 810, 1405, 1220, 497, 39, -24, },
+ {-21, 163, 798, 1401, 1229, 507, 42, -24, },
+ {-22, 157, 786, 1396, 1238, 518, 46, -24, },
+ {-22, 151, 774, 1392, 1247, 529, 49, -24, },
+ {-22, 144, 762, 1387, 1255, 540, 53, -24, },
+ {-23, 139, 750, 1382, 1264, 552, 57, -24, },
+ {-23, 133, 738, 1376, 1272, 563, 60, -24, },
+ {-23, 127, 726, 1371, 1280, 574, 64, -24, },
+ {-23, 121, 714, 1365, 1288, 586, 69, -24, },
+ {-23, 116, 703, 1359, 1296, 597, 73, -24, },
+ {-24, 111, 691, 1353, 1304, 609, 77, -24, },
+ {-24, 105, 679, 1346, 1312, 620, 81, -24, },
+ {-24, 100, 667, 1340, 1319, 632, 86, -24, },
+ {-24, 96, 655, 1333, 1326, 644, 91, -24, },
+ {-24, 91, 644, 1326, 1333, 655, 96, -24, },
+ {-24, 86, 632, 1319, 1340, 667, 100, -24, },
+ {-24, 81, 620, 1312, 1346, 679, 105, -24, },
+ {-24, 77, 609, 1304, 1353, 691, 111, -24, },
+ {-24, 73, 597, 1296, 1359, 703, 116, -23, },
+ {-24, 69, 586, 1288, 1365, 714, 121, -23, },
+ {-24, 64, 574, 1280, 1371, 726, 127, -23, },
+ {-24, 60, 563, 1272, 1376, 738, 133, -23, },
+ {-24, 57, 552, 1264, 1382, 750, 139, -23, },
+ {-24, 53, 540, 1255, 1387, 762, 144, -22, },
+ {-24, 49, 529, 1247, 1392, 774, 151, -22, },
+ {-24, 46, 518, 1238, 1396, 786, 157, -22, },
+ {-24, 42, 507, 1229, 1401, 798, 163, -21, },
+ {-24, 39, 497, 1220, 1405, 810, 170, -21, },
+ {-24, 36, 486, 1210, 1409, 822, 176, -20, },
+ {-24, 33, 475, 1201, 1413, 834, 183, -20, },
+ {-24, 30, 464, 1191, 1417, 846, 190, -19, },
+ {-24, 27, 454, 1182, 1420, 858, 197, -19, },
+ {-24, 24, 443, 1172, 1424, 870, 204, -18, },
+ {-24, 22, 433, 1162, 1427, 882, 211, -17, },
+ {-24, 19, 423, 1152, 1430, 894, 219, -17, },
+ {-24, 17, 413, 1142, 1432, 906, 226, -16, },
+ {-24, 14, 403, 1131, 1434, 918, 234, -15, },
+ {-24, 12, 393, 1121, 1437, 930, 242, -14, },
+ {-24, 10, 383, 1110, 1438, 942, 250, -13, },
+ {-24, 8, 373, 1100, 1440, 953, 258, -12, },
+ {-24, 6, 364, 1089, 1441, 965, 266, -11, },
+ {-24, 4, 354, 1078, 1443, 977, 274, -10, },
+ {-24, 2, 345, 1067, 1444, 988, 282, -9, },
+ {-24, 0, 336, 1056, 1444, 1000, 291, -7, },
+ {-24, -1, 326, 1045, 1445, 1011, 300, -6, },
+ {-24, -3, 317, 1034, 1445, 1023, 309, -5, },
+};
+
+static const u16
+xhsc_coeff_taps10[XSCALER_MAX_PHASES][XV_SCALER_TAPS_10] = {
+ {59, 224, 507, 790, 911, 793, 512, 227, 61, 13, },
+ {58, 220, 502, 786, 911, 797, 516, 231, 62, 13, },
+ {56, 216, 497, 783, 911, 800, 521, 235, 64, 13, },
+ {55, 213, 492, 779, 910, 804, 526, 238, 65, 13, },
+ {54, 209, 487, 775, 910, 807, 531, 242, 67, 14, },
+ {52, 206, 482, 772, 910, 810, 536, 246, 69, 14, },
+ {51, 202, 477, 768, 909, 813, 541, 250, 70, 14, },
+ {50, 199, 473, 764, 909, 817, 545, 254, 72, 14, },
+ {48, 195, 468, 760, 908, 820, 550, 258, 74, 15, },
+ {47, 192, 463, 756, 908, 823, 555, 262, 76, 15, },
+ {46, 188, 458, 752, 907, 826, 560, 266, 78, 15, },
+ {45, 185, 453, 748, 906, 829, 565, 270, 79, 16, },
+ {44, 182, 448, 744, 906, 832, 569, 274, 81, 16, },
+ {42, 179, 444, 740, 905, 835, 574, 278, 83, 16, },
+ {41, 175, 439, 736, 904, 837, 579, 282, 85, 17, },
+ {40, 172, 434, 732, 903, 840, 584, 286, 87, 17, },
+ {39, 169, 429, 728, 902, 843, 589, 290, 89, 18, },
+ {38, 166, 425, 724, 901, 846, 593, 294, 91, 18, },
+ {37, 163, 420, 720, 900, 848, 598, 298, 93, 18, },
+ {36, 160, 415, 716, 899, 851, 603, 302, 95, 19, },
+ {35, 157, 410, 711, 897, 854, 608, 307, 98, 19, },
+ {34, 154, 406, 707, 896, 856, 612, 311, 100, 20, },
+ {33, 151, 401, 703, 895, 859, 617, 315, 102, 20, },
+ {33, 148, 396, 698, 893, 861, 622, 320, 104, 21, },
+ {32, 145, 392, 694, 892, 863, 626, 324, 107, 21, },
+ {31, 142, 387, 690, 890, 866, 631, 328, 109, 22, },
+ {30, 140, 382, 685, 889, 868, 636, 333, 111, 23, },
+ {29, 137, 378, 681, 887, 870, 640, 337, 114, 23, },
+ {28, 134, 373, 677, 886, 872, 645, 342, 116, 24, },
+ {28, 131, 369, 672, 884, 874, 649, 346, 119, 24, },
+ {27, 129, 364, 668, 882, 876, 654, 350, 121, 25, },
+ {26, 126, 359, 663, 880, 878, 659, 355, 124, 26, },
+ {26, 124, 355, 659, 878, 880, 663, 359, 126, 26, },
+ {25, 121, 350, 654, 876, 882, 668, 364, 129, 27, },
+ {24, 119, 346, 649, 874, 884, 672, 369, 131, 28, },
+ {24, 116, 342, 645, 872, 886, 677, 373, 134, 28, },
+ {23, 114, 337, 640, 870, 887, 681, 378, 137, 29, },
+ {23, 111, 333, 636, 868, 889, 685, 382, 140, 30, },
+ {22, 109, 328, 631, 866, 890, 690, 387, 142, 31, },
+ {21, 107, 324, 626, 863, 892, 694, 392, 145, 32, },
+ {21, 104, 320, 622, 861, 893, 698, 396, 148, 33, },
+ {20, 102, 315, 617, 859, 895, 703, 401, 151, 33, },
+ {20, 100, 311, 612, 856, 896, 707, 406, 154, 34, },
+ {19, 98, 307, 608, 854, 897, 711, 410, 157, 35, },
+ {19, 95, 302, 603, 851, 899, 716, 415, 160, 36, },
+ {18, 93, 298, 598, 848, 900, 720, 420, 163, 37, },
+ {18, 91, 294, 593, 846, 901, 724, 425, 166, 38, },
+ {18, 89, 290, 589, 843, 902, 728, 429, 169, 39, },
+ {17, 87, 286, 584, 840, 903, 732, 434, 172, 40, },
+ {17, 85, 282, 579, 837, 904, 736, 439, 175, 41, },
+ {16, 83, 278, 574, 835, 905, 740, 444, 179, 42, },
+ {16, 81, 274, 569, 832, 906, 744, 448, 182, 44, },
+ {16, 79, 270, 565, 829, 906, 748, 453, 185, 45, },
+ {15, 78, 266, 560, 826, 907, 752, 458, 188, 46, },
+ {15, 76, 262, 555, 823, 908, 756, 463, 192, 47, },
+ {15, 74, 258, 550, 820, 908, 760, 468, 195, 48, },
+ {14, 72, 254, 545, 817, 909, 764, 473, 199, 50, },
+ {14, 70, 250, 541, 813, 909, 768, 477, 202, 51, },
+ {14, 69, 246, 536, 810, 910, 772, 482, 206, 52, },
+ {14, 67, 242, 531, 807, 910, 775, 487, 209, 54, },
+ {13, 65, 238, 526, 804, 910, 779, 492, 213, 55, },
+ {13, 64, 235, 521, 800, 911, 783, 497, 216, 56, },
+ {13, 62, 231, 516, 797, 911, 786, 502, 220, 58, },
+ {13, 61, 227, 512, 793, 911, 790, 507, 224, 59, },
+};
+
+static const u16
+xhsc_coeff_taps12[XSCALER_MAX_PHASES][XV_SCALER_TAPS_12] = {
+ {48, 143, 307, 504, 667, 730, 669, 507, 310, 145, 49, 18, },
+ {47, 141, 304, 501, 665, 730, 670, 510, 313, 147, 50, 18, },
+ {46, 138, 301, 498, 663, 730, 672, 513, 316, 149, 51, 18, },
+ {45, 136, 298, 495, 661, 730, 674, 516, 319, 151, 52, 18, },
+ {44, 134, 295, 492, 659, 730, 676, 519, 322, 153, 53, 18, },
+ {44, 132, 292, 489, 657, 730, 677, 522, 325, 155, 54, 18, },
+ {43, 130, 289, 486, 655, 729, 679, 525, 328, 157, 55, 19, },
+ {42, 129, 287, 483, 653, 729, 681, 528, 331, 160, 56, 19, },
+ {41, 127, 284, 480, 651, 729, 683, 531, 334, 162, 57, 19, },
+ {40, 125, 281, 477, 648, 729, 684, 534, 337, 164, 58, 19, },
+ {40, 123, 278, 474, 646, 728, 686, 537, 340, 166, 59, 20, },
+ {39, 121, 275, 471, 644, 728, 687, 539, 343, 169, 60, 20, },
+ {38, 119, 272, 468, 642, 727, 689, 542, 346, 171, 61, 20, },
+ {37, 117, 269, 465, 640, 727, 690, 545, 349, 173, 62, 20, },
+ {37, 115, 266, 461, 638, 727, 692, 548, 353, 175, 63, 21, },
+ {36, 114, 264, 458, 635, 726, 693, 551, 356, 178, 65, 21, },
+ {35, 112, 261, 455, 633, 726, 695, 554, 359, 180, 66, 21, },
+ {35, 110, 258, 452, 631, 725, 696, 556, 362, 183, 67, 21, },
+ {34, 108, 255, 449, 628, 724, 698, 559, 365, 185, 68, 22, },
+ {33, 107, 252, 446, 626, 724, 699, 562, 368, 187, 69, 22, },
+ {33, 105, 250, 443, 624, 723, 700, 565, 371, 190, 71, 22, },
+ {32, 103, 247, 440, 621, 723, 702, 567, 374, 192, 72, 23, },
+ {32, 101, 244, 437, 619, 722, 703, 570, 377, 195, 73, 23, },
+ {31, 100, 241, 433, 617, 721, 704, 573, 380, 197, 75, 23, },
+ {31, 98, 239, 430, 614, 720, 705, 576, 383, 200, 76, 24, },
+ {30, 97, 236, 427, 612, 720, 707, 578, 387, 202, 77, 24, },
+ {29, 95, 233, 424, 609, 719, 708, 581, 390, 205, 79, 24, },
+ {29, 93, 231, 421, 607, 718, 709, 584, 393, 207, 80, 25, },
+ {28, 92, 228, 418, 604, 717, 710, 586, 396, 210, 81, 25, },
+ {28, 90, 225, 415, 602, 716, 711, 589, 399, 212, 83, 26, },
+ {27, 89, 223, 412, 599, 715, 712, 591, 402, 215, 84, 26, },
+ {27, 87, 220, 408, 597, 714, 713, 594, 405, 217, 86, 27, },
+ {27, 86, 217, 405, 594, 713, 714, 597, 408, 220, 87, 27, },
+ {26, 84, 215, 402, 591, 712, 715, 599, 412, 223, 89, 27, },
+ {26, 83, 212, 399, 589, 711, 716, 602, 415, 225, 90, 28, },
+ {25, 81, 210, 396, 586, 710, 717, 604, 418, 228, 92, 28, },
+ {25, 80, 207, 393, 584, 709, 718, 607, 421, 231, 93, 29, },
+ {24, 79, 205, 390, 581, 708, 719, 609, 424, 233, 95, 29, },
+ {24, 77, 202, 387, 578, 707, 720, 612, 427, 236, 97, 30, },
+ {24, 76, 200, 383, 576, 705, 720, 614, 430, 239, 98, 31, },
+ {23, 75, 197, 380, 573, 704, 721, 617, 433, 241, 100, 31, },
+ {23, 73, 195, 377, 570, 703, 722, 619, 437, 244, 101, 32, },
+ {23, 72, 192, 374, 567, 702, 723, 621, 440, 247, 103, 32, },
+ {22, 71, 190, 371, 565, 700, 723, 624, 443, 250, 105, 33, },
+ {22, 69, 187, 368, 562, 699, 724, 626, 446, 252, 107, 33, },
+ {22, 68, 185, 365, 559, 698, 724, 628, 449, 255, 108, 34, },
+ {21, 67, 183, 362, 556, 696, 725, 631, 452, 258, 110, 35, },
+ {21, 66, 180, 359, 554, 695, 726, 633, 455, 261, 112, 35, },
+ {21, 65, 178, 356, 551, 693, 726, 635, 458, 264, 114, 36, },
+ {21, 63, 175, 353, 548, 692, 727, 638, 461, 266, 115, 37, },
+ {20, 62, 173, 349, 545, 690, 727, 640, 465, 269, 117, 37, },
+ {20, 61, 171, 346, 542, 689, 727, 642, 468, 272, 119, 38, },
+ {20, 60, 169, 343, 539, 687, 728, 644, 471, 275, 121, 39, },
+ {20, 59, 166, 340, 537, 686, 728, 646, 474, 278, 123, 40, },
+ {19, 58, 164, 337, 534, 684, 729, 648, 477, 281, 125, 40, },
+ {19, 57, 162, 334, 531, 683, 729, 651, 480, 284, 127, 41, },
+ {19, 56, 160, 331, 528, 681, 729, 653, 483, 287, 129, 42, },
+ {19, 55, 157, 328, 525, 679, 729, 655, 486, 289, 130, 43, },
+ {18, 54, 155, 325, 522, 677, 730, 657, 489, 292, 132, 44, },
+ {18, 53, 153, 322, 519, 676, 730, 659, 492, 295, 134, 44, },
+ {18, 52, 151, 319, 516, 674, 730, 661, 495, 298, 136, 45, },
+ {18, 51, 149, 316, 513, 672, 730, 663, 498, 301, 138, 46, },
+ {18, 50, 147, 313, 510, 670, 730, 665, 501, 304, 141, 47, },
+ {18, 49, 145, 310, 507, 669, 730, 667, 504, 307, 143, 48, },
+};
+
+/* V-scaler coefficients for 6, 8, 10 and 12 tap filters */
+static const u16
+xvsc_coeff_taps6[XSCALER_MAX_PHASES][XV_SCALER_TAPS_6] = {
+ {-132, 236, 3824, 236, -132, 64, },
+ {-116, 184, 3816, 292, -144, 64, },
+ {-100, 132, 3812, 348, -160, 64, },
+ {-88, 84, 3808, 404, -176, 64, },
+ {-72, 36, 3796, 464, -192, 64, },
+ {-60, -8, 3780, 524, -208, 68, },
+ {-48, -52, 3768, 588, -228, 68, },
+ {-32, -96, 3748, 652, -244, 68, },
+ {-20, -136, 3724, 716, -260, 72, },
+ {-8, -172, 3696, 784, -276, 72, },
+ {0, -208, 3676, 848, -292, 72, },
+ {12, -244, 3640, 920, -308, 76, },
+ {20, -276, 3612, 988, -324, 76, },
+ {32, -304, 3568, 1060, -340, 80, },
+ {40, -332, 3532, 1132, -356, 80, },
+ {48, -360, 3492, 1204, -372, 84, },
+ {56, -384, 3448, 1276, -388, 88, },
+ {64, -408, 3404, 1352, -404, 88, },
+ {72, -428, 3348, 1428, -416, 92, },
+ {76, -448, 3308, 1500, -432, 92, },
+ {84, -464, 3248, 1576, -444, 96, },
+ {88, -480, 3200, 1652, -460, 96, },
+ {92, -492, 3140, 1728, -472, 100, },
+ {96, -504, 3080, 1804, -484, 104, },
+ {100, -516, 3020, 1880, -492, 104, },
+ {104, -524, 2956, 1960, -504, 104, },
+ {104, -532, 2892, 2036, -512, 108, },
+ {108, -540, 2832, 2108, -520, 108, },
+ {108, -544, 2764, 2184, -528, 112, },
+ {112, -544, 2688, 2260, -532, 112, },
+ {112, -548, 2624, 2336, -540, 112, },
+ {112, -548, 2556, 2408, -544, 112, },
+ {112, -544, 2480, 2480, -544, 112, },
+ {112, -544, 2408, 2556, -548, 112, },
+ {112, -540, 2336, 2624, -548, 112, },
+ {112, -532, 2260, 2688, -544, 112, },
+ {112, -528, 2184, 2764, -544, 108, },
+ {108, -520, 2108, 2832, -540, 108, },
+ {108, -512, 2036, 2892, -532, 104, },
+ {104, -504, 1960, 2956, -524, 104, },
+ {104, -492, 1880, 3020, -516, 100, },
+ {104, -484, 1804, 3080, -504, 96, },
+ {100, -472, 1728, 3140, -492, 92, },
+ { 96, -460, 1652, 3200, -480, 88, },
+ { 96, -444, 1576, 3248, -464, 84, },
+ { 92, -432, 1500, 3308, -448, 76, },
+ { 92, -416, 1428, 3348, -428, 72, },
+ { 88, -404, 1352, 3404, -408, 64, },
+ { 88, -388, 1276, 3448, -384, 56, },
+ { 84, -372, 1204, 3492, -360, 48, },
+ { 80, -356, 1132, 3532, -332, 40, },
+ { 80, -340, 1060, 3568, -304, 32, },
+ { 76, -324, 988, 3612, -276, 20, },
+ { 76, -308, 920, 3640, -244, 12, },
+ { 72, -292, 848, 3676, -208, 0, },
+ { 72, -276, 784, 3696, -172, -8, },
+ { 72, -260, 716, 3724, -136, -20, },
+ { 68, -244, 652, 3748, -96, -32, },
+ { 68, -228, 588, 3768, -52, -48, },
+ { 68, -208, 524, 3780, -8, -60, },
+ { 64, -192, 464, 3796, 36, -72, },
+ { 64, -176, 404, 3808, 84, -88, },
+ { 64, -160, 348, 3812, 132, -100, },
+ { 64, -144, 292, 3816, 184, -116, }
+};
+
+static const u16
+xvsc_coeff_taps8[XSCALER_MAX_PHASES][XV_SCALER_TAPS_8] = {
+ {-5, 309, 1023, 1445, 1034, 317, -3, -24, },
+ {-6, 300, 1011, 1445, 1045, 326, -1, -24, },
+ {-7, 291, 1000, 1444, 1056, 336, 0, -24, },
+ {-9, 282, 988, 1444, 1067, 345, 2, -24, },
+ {-10, 274, 977, 1443, 1078, 354, 4, -24, },
+ {-11, 266, 965, 1441, 1089, 364, 6, -24, },
+ {-12, 258, 953, 1440, 1100, 373, 8, -24, },
+ {-13, 250, 942, 1438, 1110, 383, 10, -24, },
+ {-14, 242, 930, 1437, 1121, 393, 12, -24, },
+ {-15, 234, 918, 1434, 1131, 403, 14, -24, },
+ {-16, 226, 906, 1432, 1142, 413, 17, -24, },
+ {-17, 219, 894, 1430, 1152, 423, 19, -24, },
+ {-17, 211, 882, 1427, 1162, 433, 22, -24, },
+ {-18, 204, 870, 1424, 1172, 443, 24, -24, },
+ {-19, 197, 858, 1420, 1182, 454, 27, -24, },
+ {-19, 190, 846, 1417, 1191, 464, 30, -24, },
+ {-20, 183, 834, 1413, 1201, 475, 33, -24, },
+ {-20, 176, 822, 1409, 1210, 486, 36, -24, },
+ {-21, 170, 810, 1405, 1220, 497, 39, -24, },
+ {-21, 163, 798, 1401, 1229, 507, 42, -24, },
+ {-22, 157, 786, 1396, 1238, 518, 46, -24, },
+ {-22, 151, 774, 1392, 1247, 529, 49, -24, },
+ {-22, 144, 762, 1387, 1255, 540, 53, -24, },
+ {-23, 139, 750, 1382, 1264, 552, 57, -24, },
+ {-23, 133, 738, 1376, 1272, 563, 60, -24, },
+ {-23, 127, 726, 1371, 1280, 574, 64, -24, },
+ {-23, 121, 714, 1365, 1288, 586, 69, -24, },
+ {-23, 116, 703, 1359, 1296, 597, 73, -24, },
+ {-24, 111, 691, 1353, 1304, 609, 77, -24, },
+ {-24, 105, 679, 1346, 1312, 620, 81, -24, },
+ {-24, 100, 667, 1340, 1319, 632, 86, -24, },
+ {-24, 96, 655, 1333, 1326, 644, 91, -24, },
+ {-24, 91, 644, 1326, 1333, 655, 96, -24, },
+ {-24, 86, 632, 1319, 1340, 667, 100, -24, },
+ {-24, 81, 620, 1312, 1346, 679, 105, -24, },
+ {-24, 77, 609, 1304, 1353, 691, 111, -24, },
+ {-24, 73, 597, 1296, 1359, 703, 116, -23, },
+ {-24, 69, 586, 1288, 1365, 714, 121, -23, },
+ {-24, 64, 574, 1280, 1371, 726, 127, -23, },
+ {-24, 60, 563, 1272, 1376, 738, 133, -23, },
+ {-24, 57, 552, 1264, 1382, 750, 139, -23, },
+ {-24, 53, 540, 1255, 1387, 762, 144, -22, },
+ {-24, 49, 529, 1247, 1392, 774, 151, -22, },
+ {-24, 46, 518, 1238, 1396, 786, 157, -22, },
+ {-24, 42, 507, 1229, 1401, 798, 163, -21, },
+ {-24, 39, 497, 1220, 1405, 810, 170, -21, },
+ {-24, 36, 486, 1210, 1409, 822, 176, -20, },
+ {-24, 33, 475, 1201, 1413, 834, 183, -20, },
+ {-24, 30, 464, 1191, 1417, 846, 190, -19, },
+ {-24, 27, 454, 1182, 1420, 858, 197, -19, },
+ {-24, 24, 443, 1172, 1424, 870, 204, -18, },
+ {-24, 22, 433, 1162, 1427, 882, 211, -17, },
+ {-24, 19, 423, 1152, 1430, 894, 219, -17, },
+ {-24, 17, 413, 1142, 1432, 906, 226, -16, },
+ {-24, 14, 403, 1131, 1434, 918, 234, -15, },
+ {-24, 12, 393, 1121, 1437, 930, 242, -14, },
+ {-24, 10, 383, 1110, 1438, 942, 250, -13, },
+ {-24, 8, 373, 1100, 1440, 953, 258, -12, },
+ {-24, 6, 364, 1089, 1441, 965, 266, -11, },
+ {-24, 4, 354, 1078, 1443, 977, 274, -10, },
+ {-24, 2, 345, 1067, 1444, 988, 282, -9, },
+ {-24, 0, 336, 1056, 1444, 1000, 291, -7, },
+ {-24, -1, 326, 1045, 1445, 1011, 300, -6, },
+ {-24, -3, 317, 1034, 1445, 1023, 309, -5, },
+};
+
+static const u16
+xvsc_coeff_taps10[XSCALER_MAX_PHASES][XV_SCALER_TAPS_10] = {
+ {59, 224, 507, 790, 911, 793, 512, 227, 61, 13, },
+ {58, 220, 502, 786, 911, 797, 516, 231, 62, 13, },
+ {56, 216, 497, 783, 911, 800, 521, 235, 64, 13, },
+ {55, 213, 492, 779, 910, 804, 526, 238, 65, 13, },
+ {54, 209, 487, 775, 910, 807, 531, 242, 67, 14, },
+ {52, 206, 482, 772, 910, 810, 536, 246, 69, 14, },
+ {51, 202, 477, 768, 909, 813, 541, 250, 70, 14, },
+ {50, 199, 473, 764, 909, 817, 545, 254, 72, 14, },
+ {48, 195, 468, 760, 908, 820, 550, 258, 74, 15, },
+ {47, 192, 463, 756, 908, 823, 555, 262, 76, 15, },
+ {46, 188, 458, 752, 907, 826, 560, 266, 78, 15, },
+ {45, 185, 453, 748, 906, 829, 565, 270, 79, 16, },
+ {44, 182, 448, 744, 906, 832, 569, 274, 81, 16, },
+ {42, 179, 444, 740, 905, 835, 574, 278, 83, 16, },
+ {41, 175, 439, 736, 904, 837, 579, 282, 85, 17, },
+ {40, 172, 434, 732, 903, 840, 584, 286, 87, 17, },
+ {39, 169, 429, 728, 902, 843, 589, 290, 89, 18, },
+ {38, 166, 425, 724, 901, 846, 593, 294, 91, 18, },
+ {37, 163, 420, 720, 900, 848, 598, 298, 93, 18, },
+ {36, 160, 415, 716, 899, 851, 603, 302, 95, 19, },
+ {35, 157, 410, 711, 897, 854, 608, 307, 98, 19, },
+ {34, 154, 406, 707, 896, 856, 612, 311, 100, 20, },
+ {33, 151, 401, 703, 895, 859, 617, 315, 102, 20, },
+ {33, 148, 396, 698, 893, 861, 622, 320, 104, 21, },
+ {32, 145, 392, 694, 892, 863, 626, 324, 107, 21, },
+ {31, 142, 387, 690, 890, 866, 631, 328, 109, 22, },
+ {30, 140, 382, 685, 889, 868, 636, 333, 111, 23, },
+ {29, 137, 378, 681, 887, 870, 640, 337, 114, 23, },
+ {28, 134, 373, 677, 886, 872, 645, 342, 116, 24, },
+ {28, 131, 369, 672, 884, 874, 649, 346, 119, 24, },
+ {27, 129, 364, 668, 882, 876, 654, 350, 121, 25, },
+ {26, 126, 359, 663, 880, 878, 659, 355, 124, 26, },
+ {26, 124, 355, 659, 878, 880, 663, 359, 126, 26, },
+ {25, 121, 350, 654, 876, 882, 668, 364, 129, 27, },
+ {24, 119, 346, 649, 874, 884, 672, 369, 131, 28, },
+ {24, 116, 342, 645, 872, 886, 677, 373, 134, 28, },
+ {23, 114, 337, 640, 870, 887, 681, 378, 137, 29, },
+ {23, 111, 333, 636, 868, 889, 685, 382, 140, 30, },
+ {22, 109, 328, 631, 866, 890, 690, 387, 142, 31, },
+ {21, 107, 324, 626, 863, 892, 694, 392, 145, 32, },
+ {21, 104, 320, 622, 861, 893, 698, 396, 148, 33, },
+ {20, 102, 315, 617, 859, 895, 703, 401, 151, 33, },
+ {20, 100, 311, 612, 856, 896, 707, 406, 154, 34, },
+ {19, 98, 307, 608, 854, 897, 711, 410, 157, 35, },
+ {19, 95, 302, 603, 851, 899, 716, 415, 160, 36, },
+ {18, 93, 298, 598, 848, 900, 720, 420, 163, 37, },
+ {18, 91, 294, 593, 846, 901, 724, 425, 166, 38, },
+ {18, 89, 290, 589, 843, 902, 728, 429, 169, 39, },
+ {17, 87, 286, 584, 840, 903, 732, 434, 172, 40, },
+ {17, 85, 282, 579, 837, 904, 736, 439, 175, 41, },
+ {16, 83, 278, 574, 835, 905, 740, 444, 179, 42, },
+ {16, 81, 274, 569, 832, 906, 744, 448, 182, 44, },
+ {16, 79, 270, 565, 829, 906, 748, 453, 185, 45, },
+ {15, 78, 266, 560, 826, 907, 752, 458, 188, 46, },
+ {15, 76, 262, 555, 823, 908, 756, 463, 192, 47, },
+ {15, 74, 258, 550, 820, 908, 760, 468, 195, 48, },
+ {14, 72, 254, 545, 817, 909, 764, 473, 199, 50, },
+ {14, 70, 250, 541, 813, 909, 768, 477, 202, 51, },
+ {14, 69, 246, 536, 810, 910, 772, 482, 206, 52, },
+ {14, 67, 242, 531, 807, 910, 775, 487, 209, 54, },
+ {13, 65, 238, 526, 804, 910, 779, 492, 213, 55, },
+ {13, 64, 235, 521, 800, 911, 783, 497, 216, 56, },
+ {13, 62, 231, 516, 797, 911, 786, 502, 220, 58, },
+ {13, 61, 227, 512, 793, 911, 790, 507, 224, 59, },
+};
+
+static const u16
+xvsc_coeff_taps12[XSCALER_MAX_PHASES][XV_SCALER_TAPS_12] = {
+ {48, 143, 307, 504, 667, 730, 669, 507, 310, 145, 49, 18, },
+ {47, 141, 304, 501, 665, 730, 670, 510, 313, 147, 50, 18, },
+ {46, 138, 301, 498, 663, 730, 672, 513, 316, 149, 51, 18, },
+ {45, 136, 298, 495, 661, 730, 674, 516, 319, 151, 52, 18, },
+ {44, 134, 295, 492, 659, 730, 676, 519, 322, 153, 53, 18, },
+ {44, 132, 292, 489, 657, 730, 677, 522, 325, 155, 54, 18, },
+ {43, 130, 289, 486, 655, 729, 679, 525, 328, 157, 55, 19, },
+ {42, 129, 287, 483, 653, 729, 681, 528, 331, 160, 56, 19, },
+ {41, 127, 284, 480, 651, 729, 683, 531, 334, 162, 57, 19, },
+ {40, 125, 281, 477, 648, 729, 684, 534, 337, 164, 58, 19, },
+ {40, 123, 278, 474, 646, 728, 686, 537, 340, 166, 59, 20, },
+ {39, 121, 275, 471, 644, 728, 687, 539, 343, 169, 60, 20, },
+ {38, 119, 272, 468, 642, 727, 689, 542, 346, 171, 61, 20, },
+ {37, 117, 269, 465, 640, 727, 690, 545, 349, 173, 62, 20, },
+ {37, 115, 266, 461, 638, 727, 692, 548, 353, 175, 63, 21, },
+ {36, 114, 264, 458, 635, 726, 693, 551, 356, 178, 65, 21, },
+ {35, 112, 261, 455, 633, 726, 695, 554, 359, 180, 66, 21, },
+ {35, 110, 258, 452, 631, 725, 696, 556, 362, 183, 67, 21, },
+ {34, 108, 255, 449, 628, 724, 698, 559, 365, 185, 68, 22, },
+ {33, 107, 252, 446, 626, 724, 699, 562, 368, 187, 69, 22, },
+ {33, 105, 250, 443, 624, 723, 700, 565, 371, 190, 71, 22, },
+ {32, 103, 247, 440, 621, 723, 702, 567, 374, 192, 72, 23, },
+ {32, 101, 244, 437, 619, 722, 703, 570, 377, 195, 73, 23, },
+ {31, 100, 241, 433, 617, 721, 704, 573, 380, 197, 75, 23, },
+ {31, 98, 239, 430, 614, 720, 705, 576, 383, 200, 76, 24, },
+ {30, 97, 236, 427, 612, 720, 707, 578, 387, 202, 77, 24, },
+ {29, 95, 233, 424, 609, 719, 708, 581, 390, 205, 79, 24, },
+ {29, 93, 231, 421, 607, 718, 709, 584, 393, 207, 80, 25, },
+ {28, 92, 228, 418, 604, 717, 710, 586, 396, 210, 81, 25, },
+ {28, 90, 225, 415, 602, 716, 711, 589, 399, 212, 83, 26, },
+ {27, 89, 223, 412, 599, 715, 712, 591, 402, 215, 84, 26, },
+ {27, 87, 220, 408, 597, 714, 713, 594, 405, 217, 86, 27, },
+ {27, 86, 217, 405, 594, 713, 714, 597, 408, 220, 87, 27, },
+ {26, 84, 215, 402, 591, 712, 715, 599, 412, 223, 89, 27, },
+ {26, 83, 212, 399, 589, 711, 716, 602, 415, 225, 90, 28, },
+ {25, 81, 210, 396, 586, 710, 717, 604, 418, 228, 92, 28, },
+ {25, 80, 207, 393, 584, 709, 718, 607, 421, 231, 93, 29, },
+ {24, 79, 205, 390, 581, 708, 719, 609, 424, 233, 95, 29, },
+ {24, 77, 202, 387, 578, 707, 720, 612, 427, 236, 97, 30, },
+ {24, 76, 200, 383, 576, 705, 720, 614, 430, 239, 98, 31, },
+ {23, 75, 197, 380, 573, 704, 721, 617, 433, 241, 100, 31, },
+ {23, 73, 195, 377, 570, 703, 722, 619, 437, 244, 101, 32, },
+ {23, 72, 192, 374, 567, 702, 723, 621, 440, 247, 103, 32, },
+ {22, 71, 190, 371, 565, 700, 723, 624, 443, 250, 105, 33, },
+ {22, 69, 187, 368, 562, 699, 724, 626, 446, 252, 107, 33, },
+ {22, 68, 185, 365, 559, 698, 724, 628, 449, 255, 108, 34, },
+ {21, 67, 183, 362, 556, 696, 725, 631, 452, 258, 110, 35, },
+ {21, 66, 180, 359, 554, 695, 726, 633, 455, 261, 112, 35, },
+ {21, 65, 178, 356, 551, 693, 726, 635, 458, 264, 114, 36, },
+ {21, 63, 175, 353, 548, 692, 727, 638, 461, 266, 115, 37, },
+ {20, 62, 173, 349, 545, 690, 727, 640, 465, 269, 117, 37, },
+ {20, 61, 171, 346, 542, 689, 727, 642, 468, 272, 119, 38, },
+ {20, 60, 169, 343, 539, 687, 728, 644, 471, 275, 121, 39, },
+ {20, 59, 166, 340, 537, 686, 728, 646, 474, 278, 123, 40, },
+ {19, 58, 164, 337, 534, 684, 729, 648, 477, 281, 125, 40, },
+ {19, 57, 162, 334, 531, 683, 729, 651, 480, 284, 127, 41, },
+ {19, 56, 160, 331, 528, 681, 729, 653, 483, 287, 129, 42, },
+ {19, 55, 157, 328, 525, 679, 729, 655, 486, 289, 130, 43, },
+ {18, 54, 155, 325, 522, 677, 730, 657, 489, 292, 132, 44, },
+ {18, 53, 153, 322, 519, 676, 730, 659, 492, 295, 134, 44, },
+ {18, 52, 151, 319, 516, 674, 730, 661, 495, 298, 136, 45, },
+ {18, 51, 149, 316, 513, 672, 730, 663, 498, 301, 138, 46, },
+ {18, 50, 147, 313, 510, 670, 730, 665, 501, 304, 141, 47, },
+ {18, 49, 145, 310, 507, 669, 730, 667, 504, 307, 143, 48, },
+};
+
+/* Mask definitions for Low and high 16 bits in a 32 bit number */
+#define XHSC_MASK_LOW_16BITS GENMASK(15, 0)
+#define XHSC_MASK_HIGH_16BITS GENMASK(31, 16)
+#define XHSC_MASK_LOW_32BITS GENMASK(31, 0)
+
+static void
+xv_hscaler_load_ext_coeff(struct xm2m_scaler_hw *xscaler,
+ const short *coeff, u32 ntaps)
+{
+ unsigned int i, j, pad, offset;
+ const u32 nphases = XSCALER_MAX_PHASES;
+
+ /* Determine if coefficient needs padding (effective vs. max taps) */
+ pad = XV_SCALER_MAX_TAPS - ntaps;
+ offset = pad >> 1;
+
+ memset(xscaler->hscaler_coeff, 0, sizeof(xscaler->hscaler_coeff));
+
+ /* Load coefficients into scaler coefficient table */
+ for (i = 0; i < nphases; i++) {
+ for (j = 0; j < ntaps; ++j)
+ xscaler->hscaler_coeff[i][j + offset] =
+ coeff[i * ntaps + j];
+ }
+}
+
+#define XSCALER_BITSHIFT_16 (16)
+static void xv_hscaler_set_coeff(struct xm2m_scaler_hw *xscaler,
+ const u32 base_addr)
+{
+ int val, i, j, offset, rd_indx;
+ u32 ntaps = xscaler->num_taps;
+ const u32 nphases = XSCALER_MAX_PHASES;
+
+ offset = (XV_SCALER_MAX_TAPS - ntaps) / 2;
+ for (i = 0; i < nphases; i++) {
+ for (j = 0; j < ntaps / 2; j++) {
+ rd_indx = j * 2 + offset;
+ val = (xscaler->hscaler_coeff[i][rd_indx + 1] <<
+ XSCALER_BITSHIFT_16) |
+ (xscaler->hscaler_coeff[i][rd_indx] &
+ XHSC_MASK_LOW_16BITS);
+ xvip_write(xscaler, base_addr +
+ ((i * ntaps / 2 + j) * 4), val);
+ }
+ }
+}
+
+static void
+xv_vscaler_load_ext_coeff(struct xm2m_scaler_hw *xscaler,
+ const short *coeff, const u32 ntaps)
+{
+ int i, j, pad, offset;
+ const u32 nphases = XSCALER_MAX_PHASES;
+
+ /* Determine if coefficient needs padding (effective vs. max taps) */
+ pad = XV_SCALER_MAX_TAPS - ntaps;
+ offset = pad ? (pad >> 1) : 0;
+
+ /* Zero Entire Array */
+ memset(xscaler->vscaler_coeff, 0, sizeof(xscaler->vscaler_coeff));
+
+ /* Load User defined coefficients into scaler coefficient table */
+ for (i = 0; i < nphases; i++) {
+ for (j = 0; j < ntaps; ++j)
+ xscaler->vscaler_coeff[i][j + offset] =
+ coeff[i * ntaps + j];
+ }
+}
+
+#define XVSC_MASK_LOW_16BITS GENMASK(15, 0)
+static void xv_vscaler_set_coeff(struct xm2m_scaler_hw *xscaler,
+ const u32 base_addr)
+{
+ int val, i, j, offset, rd_indx;
+ u32 ntaps = xscaler->num_taps;
+ const u32 nphases = XSCALER_MAX_PHASES;
+
+ offset = (XV_SCALER_MAX_TAPS - ntaps) / 2;
+
+ for (i = 0; i < nphases; i++) {
+ for (j = 0; j < ntaps / 2; j++) {
+ rd_indx = j * 2 + offset;
+ val = (xscaler->vscaler_coeff[i][rd_indx + 1] <<
+ XSCALER_BITSHIFT_16) |
+ (xscaler->vscaler_coeff[i][rd_indx] &
+ XVSC_MASK_LOW_16BITS);
+ xvip_write(xscaler,
+ base_addr + ((i * ntaps / 2 + j) * 4), val);
+ }
+ }
+}
+
+void xm2mvsc_initialize_coeff_banks(struct xm2m_scaler_hw *hw)
+{
+ /* Bank 0 is init as 6 tap filter for 6, 8, 10 & 12 tap filters */
+ xv_hscaler_load_ext_coeff(hw, &xhsc_coeff_taps6[0][0],
+ XV_SCALER_TAPS_6);
+ xv_hscaler_set_coeff(hw, XM2MVSC_HFLTCOEFF(0));
+ xv_vscaler_load_ext_coeff(hw, &xvsc_coeff_taps6[0][0],
+ XV_SCALER_TAPS_6);
+ xv_vscaler_set_coeff(hw, XM2MVSC_VFLTCOEFF(0));
+ dev_dbg(hw->dev, "%s: Init Bank 0", __func__);
+ /* Bank 1 is init as 8 tap filter for 8, 10 & 12 tap filters */
+ if (hw->num_taps == XV_SCALER_TAPS_8 ||
+ hw->num_taps == XV_SCALER_TAPS_10 ||
+ hw->num_taps == XV_SCALER_TAPS_12) {
+ xv_hscaler_load_ext_coeff(hw, &xhsc_coeff_taps8[0][0],
+ XV_SCALER_TAPS_8);
+ xv_hscaler_set_coeff(hw, XM2MVSC_HFLTCOEFF(1));
+ xv_vscaler_load_ext_coeff(hw, &xvsc_coeff_taps8[0][0],
+ XV_SCALER_TAPS_8);
+ xv_vscaler_set_coeff(hw, XM2MVSC_VFLTCOEFF(1));
+ dev_dbg(hw->dev, "%s: Init Bank 1", __func__);
+ }
+ /* Bank 2 is init as 8 tap filter for 10 & 12 tap filters */
+ if (hw->num_taps == XV_SCALER_TAPS_10 ||
+ hw->num_taps == XV_SCALER_TAPS_12) {
+ xv_hscaler_load_ext_coeff(hw, &xhsc_coeff_taps10[0][0],
+ XV_SCALER_TAPS_10);
+ xv_hscaler_set_coeff(hw, XM2MVSC_HFLTCOEFF(2));
+ xv_vscaler_load_ext_coeff(hw, &xvsc_coeff_taps10[0][0],
+ XV_SCALER_TAPS_10);
+ xv_vscaler_set_coeff(hw, XM2MVSC_VFLTCOEFF(2));
+ dev_dbg(hw->dev, "%s: Init Bank 2", __func__);
+ }
+ /* Bank 3 is init as 8 tap filter for 12 tap filters */
+ if (hw->num_taps == XV_SCALER_TAPS_12) {
+ xv_hscaler_load_ext_coeff(hw, &xhsc_coeff_taps12[0][0],
+ XV_SCALER_TAPS_12);
+ xv_hscaler_set_coeff(hw, XM2MVSC_HFLTCOEFF(3));
+ xv_vscaler_load_ext_coeff(hw, &xvsc_coeff_taps12[0][0],
+ XV_SCALER_TAPS_12);
+ xv_vscaler_set_coeff(hw, XM2MVSC_VFLTCOEFF(3));
+ dev_dbg(hw->dev, "%s: Init Bank 2", __func__);
+ }
+}
+
+/**
+ * xm2mvsc_select_coeff_bank - Selection of Scaler coefficients of operation
+ * @xscaler: Scaler device information
+ * @width_in: Width of input video
+ * @width_out: Width of desired output video
+ * @height_in : Height of the input video
+ * @height_out : Height of the output video
+ * @filter_bank : Filter bank to be set by the function
+ *
+ * There are instances when a N-tap filter might operate in an M-tap
+ * configuration where N > M.
+ *
+ * For example :
+ * Depending on the ratio of scaling (while downscaling), a 12-tap
+ * filter may operate with 10 tap coefficients and zero-pads the remaining
+ * coefficients.
+ *
+ * While upscaling the driver will program 6-tap filter coefficients
+ * in any N-tap configurations (for N >= 6).
+ *
+ * This selection is adopted by the as it gives optimal
+ * video output determined by repeated testing of the IP
+ *
+ * Return: Will return 0 if successful. Returns -EINVAL on an unsupported
+ * H-scaler number of taps.
+ */
+static void xm2mvsc_select_coeff_bank(struct xm2m_scaler_hw *xscaler,
+ const u32 width_in,
+ const u32 width_out,
+ const u32 height_in,
+ const u32 height_out,
+ u8 *filter_bank)
+{
+ u16 hscale_ratio;
+ u16 vscale_ratio;
+ u16 selection_ratio;
+
+ hscale_ratio = (width_in * 10) / width_out;
+ vscale_ratio = (height_in * 10) / height_out;
+ selection_ratio = (hscale_ratio > vscale_ratio ?
+ hscale_ratio : vscale_ratio);
+ /*
+ * Scale Down Mode will use dynamic filter selection logic
+ * Scale Up Mode (including 1:1) will always use 6 tap filter
+ */
+ if (selection_ratio > 10) {
+ switch (xscaler->num_taps) {
+ case XV_SCALER_TAPS_6:
+ *filter_bank = FILTER_BANK_TAPS_6;
+ break;
+ case XV_SCALER_TAPS_8:
+ if (selection_ratio > 15)
+ *filter_bank = FILTER_BANK_TAPS_8;
+ else
+ *filter_bank = FILTER_BANK_TAPS_6;
+ break;
+ case XV_SCALER_TAPS_10:
+ if (selection_ratio > 25)
+ *filter_bank = FILTER_BANK_TAPS_10;
+ else if (selection_ratio > 15)
+ *filter_bank = FILTER_BANK_TAPS_8;
+ else
+ *filter_bank = FILTER_BANK_TAPS_6;
+ break;
+ case XV_SCALER_TAPS_12:
+ if (selection_ratio > 35)
+ *filter_bank = FILTER_BANK_TAPS_12;
+ else if (selection_ratio > 25)
+ *filter_bank = FILTER_BANK_TAPS_10;
+ else if (selection_ratio > 15)
+ *filter_bank = FILTER_BANK_TAPS_8;
+ else
+ *filter_bank = FILTER_BANK_TAPS_6;
+ break;
+ default:
+ /* Should never get here */
+ WARN(1, "Impossible scaler tap selection");
+ return;
+ }
+ } else {
+ *filter_bank = FILTER_BANK_TAPS_6;
+ }
+}
+
+static void xm2mvsc_set_color_format(struct xm2m_vscale_desc *desc)
+{
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_PIXELFMT_IN(desc->channel_offset),
+ desc->data.srcbuf_cft);
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_PIXELFMT_OUT(desc->channel_offset),
+ desc->data.dstbuf_cft);
+}
+
+#define STEP_PRECISION (65536)
+static void xm2mvsc_program_scaler(struct xm2m_vscale_desc *desc)
+{
+ desc->line_rate =
+ (desc->data.srcbuf_ht * STEP_PRECISION) / desc->data.dstbuf_ht;
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_LINERATE(desc->channel_offset),
+ desc->line_rate);
+ desc->pixel_rate =
+ (desc->data.srcbuf_wt * STEP_PRECISION) / desc->data.dstbuf_wt;
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_PIXELRATE(desc->channel_offset),
+ desc->pixel_rate);
+ xm2mvsc_select_coeff_bank(&desc->xm2mvsc_dev->hw,
+ desc->data.srcbuf_wt, desc->data.dstbuf_wt,
+ desc->data.srcbuf_ht, desc->data.dstbuf_ht,
+ &desc->filter_bank);
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_FILTER_BANK(desc->channel_offset),
+ desc->filter_bank);
+ xm2mvsc_set_color_format(desc);
+}
+
+void xm2mvsc_write_desc(struct xm2m_vscale_desc *desc)
+{
+ WARN(!desc, "%s : desc is NULL", __func__);
+ if (!desc)
+ return;
+ WARN(!desc->xm2mvsc_dev,
+ "%s: desc->xm2mvsc_dev is NULL for desc_id = %d",
+ __func__, desc->data.desc_id);
+ if (!desc->xm2mvsc_dev)
+ return;
+ dev_dbg(desc->xm2mvsc_dev->dev,
+ "%s: Writing desc %d with chan offset = %d",
+ __func__, desc->data.desc_id, desc->channel_offset);
+ xm2mvsc_program_scaler(desc);
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_HEIGHT_IN(desc->channel_offset),
+ desc->data.srcbuf_ht);
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_WIDTH_IN(desc->channel_offset),
+ desc->data.srcbuf_wt);
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_HEIGHT_OUT(desc->channel_offset),
+ desc->data.dstbuf_ht);
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_WIDTH_OUT(desc->channel_offset),
+ desc->data.dstbuf_wt);
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_SRC_BUF1(desc->channel_offset),
+ desc->srcbuf_addr);
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_DST_BUF1(desc->channel_offset),
+ desc->dstbuf_addr);
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_STRIDE_IN(desc->channel_offset),
+ desc->data.srcbuf_stride);
+ xvip_write(&desc->xm2mvsc_dev->hw,
+ XM2MVSC_STRIDE_OUT(desc->channel_offset),
+ desc->data.dstbuf_stride);
+}
+
+#define XM2MVSC_GLOBAL_ENABLE_IRQ BIT(0)
+#define XM2MVSC_IRQ_AP_DONE BIT(0)
+#define XM2MVSC_IRQ_AP_READY BIT(1)
+#define XM2MVSC_START_SCALING BIT(0)
+void xm2mvsc_start_scaling(const struct xm2m_scaler_hw *hw, const u8 batch_size)
+{
+ WARN(!hw, "%s: hw is NULL", __func__);
+ if (!hw)
+ return;
+ /* Enable Interrupt on IER register */
+ xvip_write(hw, XM2MVSC_IER, XM2MVSC_IRQ_AP_DONE);
+ /* Enable Global IER */
+ xvip_write(hw, XM2MVSC_GIE, XM2MVSC_GLOBAL_ENABLE_IRQ);
+ /* Write Number of Outputs */
+ xvip_write(hw, XM2MVSC_NUM_OUTS, batch_size);
+ /* Start IP with Auto-Restart Disabled */
+ xvip_write(hw, XM2MVSC_AP_CTRL, XM2MVSC_START_SCALING);
+}
+
+void xm2mvsc_log_register(const struct xm2m_scaler_hw *hw, const u8 chan_off)
+{
+ dev_dbg(hw->dev, "-------- %s : XM2MVSC HW REG Channel %d --------",
+ __func__, chan_off);
+ dev_dbg(hw->dev, "CTRL = 0x%x", xvip_read(hw, XM2MVSC_AP_CTRL));
+ dev_dbg(hw->dev, "GIE = 0x%x", xvip_read(hw, XM2MVSC_GIE));
+ dev_dbg(hw->dev, "IER = 0x%x", xvip_read(hw, XM2MVSC_IER));
+ dev_dbg(hw->dev, "ISR = 0x%x", xvip_read(hw, XM2MVSC_ISR));
+ dev_dbg(hw->dev, "Num Outs = %d", xvip_read(hw, XM2MVSC_NUM_OUTS));
+ dev_dbg(hw->dev, "SRC1 = 0x%x",
+ xvip_read(hw, XM2MVSC_SRC_BUF1(chan_off)));
+ dev_dbg(hw->dev, "SRC2 = 0x%x",
+ xvip_read(hw, XM2MVSC_SRC_BUF2(chan_off)));
+ dev_dbg(hw->dev, "HT_IN = %d",
+ xvip_read(hw, XM2MVSC_HEIGHT_IN(chan_off)));
+ dev_dbg(hw->dev, "WT_IN = %d",
+ xvip_read(hw, XM2MVSC_WIDTH_IN(chan_off)));
+ dev_dbg(hw->dev, "HT_OUT = %d",
+ xvip_read(hw, XM2MVSC_HEIGHT_OUT(chan_off)));
+ dev_dbg(hw->dev, "WT_OUT = %d",
+ xvip_read(hw, XM2MVSC_WIDTH_OUT(chan_off)));
+ dev_dbg(hw->dev, "Stride In = %d",
+ xvip_read(hw, XM2MVSC_STRIDE_IN(chan_off)));
+ dev_dbg(hw->dev, "Stride Out = %d",
+ xvip_read(hw, XM2MVSC_STRIDE_OUT(chan_off)));
+ dev_dbg(hw->dev, "Pixel Fmt In = %d",
+ xvip_read(hw, XM2MVSC_PIXELFMT_IN(chan_off)));
+ dev_dbg(hw->dev, "Pixel Fmt Out = %d",
+ xvip_read(hw, XM2MVSC_PIXELFMT_OUT(chan_off)));
+ dev_dbg(hw->dev, "PixRate = 0x%x",
+ xvip_read(hw, XM2MVSC_PIXELRATE(chan_off)));
+ dev_dbg(hw->dev, "LineRate = 0x%x",
+ xvip_read(hw, XM2MVSC_LINERATE(chan_off)));
+ dev_dbg(hw->dev, "DST1 = 0x%x",
+ xvip_read(hw, XM2MVSC_DST_BUF1(chan_off)));
+ dev_dbg(hw->dev, "DST2 = 0x%x",
+ xvip_read(hw, XM2MVSC_DST_BUF2(chan_off)));
+ dev_dbg(hw->dev, "Filter Bank = %d",
+ xvip_read(hw, XM2MVSC_FILTER_BANK(chan_off)));
+}
+
+void xm2mvsc_stop_scaling(const struct xm2m_scaler_hw *hw)
+{
+ WARN(!hw, "%s: hw is NULL", __func__);
+ if (!hw)
+ return;
+ /* Disable Interrupt on IER Register */
+ xvip_write(hw, XM2MVSC_IER, 0);
+ /* Disable Global IER */
+ xvip_write(hw, XM2MVSC_GIE, 0);
+ /* Stop IP */
+ xvip_write(hw, XM2MVSC_AP_CTRL, 0);
+}
+
+u32 xm2mvsc_get_irq_status(const struct xm2m_scaler_hw *hw)
+{
+ u32 status;
+
+ WARN_ON(!hw || IS_ERR(hw));
+ status = xvip_read(hw, XM2MVSC_ISR);
+ status &= (XM2MVSC_IRQ_AP_DONE);
+ if (status) {
+ xvip_write(hw, XM2MVSC_ISR, status);
+ return status;
+ }
+ return 0;
+}
diff --git a/drivers/staging/xm2mvscale/scaler_hw_xm2m.h b/drivers/staging/xm2mvscale/scaler_hw_xm2m.h
new file mode 100644
index 0000000..1429791
--- /dev/null
+++ b/drivers/staging/xm2mvscale/scaler_hw_xm2m.h
@@ -0,0 +1,152 @@
+/*
+ * Xilinx Memory-to-Memory Video Scaler IP
+ *
+ * Copyright (C) 2018 Xilinx, Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef __SCALER_HW_XM2M_H__
+#define __SCALER_HW_XM2M_H__
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/types.h>
+
+#include "ioctl_xm2mvsc.h"
+
+#define XSCALER_MAX_WIDTH (3840)
+#define XSCALER_MAX_HEIGHT (2160)
+#define XSCALER_MAX_PHASES (64)
+
+#define XV_SCALER_MAX_TAPS (12)
+
+#define XV_SCALER_TAPS_6 (6)
+#define XV_SCALER_TAPS_8 (8)
+#define XV_SCALER_TAPS_10 (10)
+#define XV_SCALER_TAPS_12 (12)
+
+/* Filter bank ID for various filter tap configurations */
+enum xm2mvsc_filter_bank_id {
+ FILTER_BANK_TAPS_6 = 0,
+ FILTER_BANK_TAPS_8,
+ FILTER_BANK_TAPS_10,
+ FILTER_BANK_TAPS_12,
+};
+
+#define XSCALER_BATCH_SIZE_MAX (8)
+#define XSCALER_BATCH_SIZE_MIN (1)
+
+struct xm2m_vscale_dev;
+
+/**
+ * struct xm2m_scaler_hw - Scaler Hardware Info
+ * @regs: IO mapped base address of the HW/IP
+ * @dev: Pointer to struct device instance
+ * @num_taps: Polyhphase filter taps Scaler IP
+ * @max_chan: Maximum number of Scaling Channels
+ * @max_pixels: Maximum number of pixel supported in a line
+ * @max_lines: Maximum number of lines supported in a frame
+ * @hscaler_coeff: Array of filter coefficients for the Horizontal Scaler
+ * @vscaler_coeff: Array of filter coefficients for the Vertical Scaler
+ */
+struct xm2m_scaler_hw {
+ void __iomem *regs;
+ struct device *dev;
+ u32 num_taps;
+ u32 max_chan;
+ u32 max_pixels;
+ u32 max_lines;
+ short hscaler_coeff[XSCALER_MAX_PHASES][XV_SCALER_MAX_TAPS];
+ short vscaler_coeff[XSCALER_MAX_PHASES][XV_SCALER_MAX_TAPS];
+};
+
+/**
+ * struct xm2m_vscale_desc - Video Scale Frame Descriptor
+ * @data: Data enqueued by the application
+ * @line_rate: Line rate needed by a scaling channel
+ * @pixel_rate: Pixel rate needed by a scaling channel
+ * @filter_bank: Filter Bank ID needed to source filter coefficients
+ * @channel_offset: Channel offset of the descriptor mapping to HW register
+ * @srcbuf_addr: physical address of source buffer
+ * @dstbuf_addr: physical address of destination buffer
+ * @xm2mvsc_dev: Pointer to parent xm2mvsc driver structure
+ * @node: List node to control descriptors in lists
+ * @src_kaddr: Kernel VA for source buffer allocated by the driver
+ * @dst_kaddr: Kernel VA for destination buffer allocated by the driver
+ */
+struct xm2m_vscale_desc {
+ struct xm2mvsc_qdata data;
+ u32 line_rate;
+ u32 pixel_rate;
+ u8 filter_bank;
+ u8 channel_offset;
+ dma_addr_t srcbuf_addr;
+ dma_addr_t dstbuf_addr;
+ struct xm2m_vscale_dev *xm2mvsc_dev;
+ struct list_head node;
+ void *src_kaddr;
+ void *dst_kaddr;
+};
+
+/**
+ * struct xm2m_vscale_dev - Xilinx M2M Scaler Device
+ * @dev: Pointer to struct device instance used by the driver
+ * @hw: HW/IP specific structure describing the capabilities
+ * @lock: Spinlock to protect driver data structures
+ * @pending_list: List containing descriptors not yet processed
+ * @ongoing_list: List containing descriptors that are in-flight
+ * @done_list: List containing descriptors that are done processing
+ * @free_list: List containing descriptors that need to be freed
+ * @waitq: Wait queue used by the driver
+ * @irq: IRQ number
+ * @chdev: Char device handle
+ * @id: Device instance ID
+ * @rst_gpio: GPIO reset line to bring VPSS Scaler out of reset
+ * @desc_count: Desc Count issued by the driver
+ * @user_count: Count of users who have opened the device
+ * @batch_size: Number of channel actively used in a scaling operation
+ * @ongoing_count: Number of channels already used in the ongoing operation
+ */
+struct xm2m_vscale_dev {
+ struct device *dev;
+ struct xm2m_scaler_hw hw;
+ /* Synchronize access to lists */
+ spinlock_t lock;
+ struct list_head pending_list;
+ struct list_head ongoing_list;
+ struct list_head done_list;
+ struct list_head free_list;
+ wait_queue_head_t waitq;
+ int irq;
+ struct cdev chdev;
+ u32 id;
+ struct gpio_desc *rst_gpio;
+ atomic_t desc_count;
+ atomic_t user_count;
+ u16 batch_size;
+ atomic_t ongoing_count;
+};
+
+static inline u32 xvip_read(const struct xm2m_scaler_hw *hw, const u32 addr)
+{
+ return ioread32(hw->regs + addr);
+}
+
+static inline void xvip_write(const struct xm2m_scaler_hw *hw,
+ const u32 addr, const u32 value)
+{
+ iowrite32(value, hw->regs + addr);
+}
+
+void xm2mvsc_write_desc(struct xm2m_vscale_desc *desc);
+void xm2mvsc_start_scaling(const struct xm2m_scaler_hw *hw,
+ const u8 batch_size);
+void xm2mvsc_stop_scaling(const struct xm2m_scaler_hw *hw);
+u32 xm2mvsc_get_irq_status(const struct xm2m_scaler_hw *hw);
+void xm2mvsc_log_register(const struct xm2m_scaler_hw *hw, const u8 chan_off);
+void xm2mvsc_initialize_coeff_banks(struct xm2m_scaler_hw *hw);
+
+#endif /* __XM2M_SCALER_SETUP_H__ */
diff --git a/drivers/staging/xm2mvscale/xm2m_vscale.c b/drivers/staging/xm2mvscale/xm2m_vscale.c
new file mode 100644
index 0000000..b294d31
--- /dev/null
+++ b/drivers/staging/xm2mvscale/xm2m_vscale.c
@@ -0,0 +1,768 @@
+/*
+ * Xilinx Memory-to-Memory Video Scaler IP
+ *
+ * Copyright (C) 2018 Xilinx, Inc. All rights reserved.
+ *
+ * Description:
+ * This driver is developed for the Xilinx M2M Video Scaler IP. It allows
+ * userspace to operate upon the IP and takes care of interrupt handling
+ * and framebuffer programming within the driver.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <linux/cdev.h>
+#include <linux/dma-buf.h>
+#include <linux/fs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+
+#include "scaler_hw_xm2m.h"
+#include "ioctl_xm2mvsc.h"
+
+/* Forward Declaration */
+static int xm2mvsc_ioctl_stop(struct xm2m_vscale_dev *xm2mvsc);
+
+/* Module Parameters */
+static struct class *xm2mvsc_class;
+static dev_t xm2mvsc_devt;
+static atomic_t xm2mvsc_ndevs = ATOMIC_INIT(0);
+
+#define DRIVER_NAME "xilinx-m2m-scaler"
+#define DRIVER_VERSION "0.4"
+#define DRIVER_MAX_DEV (10)
+
+static int xm2mvsc_open(struct inode *iptr, struct file *fptr)
+{
+ struct xm2m_vscale_dev *xm2mvsc;
+
+ xm2mvsc = container_of(iptr->i_cdev, struct xm2m_vscale_dev, chdev);
+ if (!xm2mvsc) {
+ pr_err("%s: failed to get xm2mvsc driver handle", __func__);
+ return -EAGAIN;
+ }
+ fptr->private_data = xm2mvsc;
+ xm2mvsc->batch_size = XSCALER_BATCH_SIZE_MIN;
+ atomic_inc(&xm2mvsc->user_count);
+ return 0;
+}
+
+static int xm2mvsc_release(struct inode *iptr, struct file *fptr)
+{
+ struct xm2m_vscale_dev *xm2mvsc;
+
+ xm2mvsc = container_of(iptr->i_cdev, struct xm2m_vscale_dev, chdev);
+ if (!xm2mvsc) {
+ pr_err("%s: failed to get xm2mvsc driver handle", __func__);
+ return -EAGAIN;
+ }
+ if (atomic_dec_and_test(&xm2mvsc->user_count)) {
+ /* Reset IP and clear driver state */
+ dev_dbg(xm2mvsc->dev,
+ "%s: Stopping and clearing device", __func__);
+ (void)xm2mvsc_ioctl_stop(xm2mvsc);
+ atomic_set(&xm2mvsc->desc_count, 0);
+ atomic_set(&xm2mvsc->ongoing_count, 0);
+ }
+ dev_dbg(xm2mvsc->dev, "%s: user count = %d",
+ __func__, atomic_read(&xm2mvsc->user_count));
+ return 0;
+}
+
+#define XM2MVSC_MAX_WIDTH (3840)
+#define XM2MVSC_MAX_HEIGHT (2160)
+#define XM2MVSC_MIN_WIDTH (32)
+#define XM2MVSC_MIN_HEIGHT (32)
+static int xm2mvsc_verify_desc(struct xm2m_vscale_desc *desc)
+{
+ if (!desc)
+ return -EIO;
+ if (desc->data.srcbuf_ht > XM2MVSC_MAX_HEIGHT ||
+ desc->data.srcbuf_ht < XM2MVSC_MIN_HEIGHT ||
+ desc->data.dstbuf_ht > XM2MVSC_MAX_HEIGHT ||
+ desc->data.dstbuf_ht < XM2MVSC_MIN_HEIGHT)
+ return -EINVAL;
+ if (desc->data.srcbuf_wt > XM2MVSC_MAX_WIDTH ||
+ desc->data.srcbuf_wt < XM2MVSC_MIN_WIDTH ||
+ desc->data.dstbuf_wt > XM2MVSC_MAX_WIDTH ||
+ desc->data.dstbuf_wt < XM2MVSC_MIN_WIDTH)
+ return -EINVAL;
+ return 0;
+}
+
+static int xm2mvsc_ioctl_batch_size(struct xm2m_vscale_dev *xm2mvsc,
+ void __user *arg)
+{
+ int ret;
+ struct xm2mvsc_batch *batch;
+
+ batch = kzalloc(sizeof(*batch), GFP_KERNEL);
+ if (!batch)
+ return -ENOMEM;
+ ret = copy_from_user(batch, arg, sizeof(*batch));
+ if (ret) {
+ dev_err(xm2mvsc->dev,
+ "%s: Failed to copy from user", __func__);
+ kfree(batch);
+ return -EFAULT;
+ }
+
+ if (!batch->batch_size || batch->batch_size > xm2mvsc->hw.max_chan) {
+ dev_err(xm2mvsc->dev,
+ "Invalid batch size passed %d", batch->batch_size);
+ kfree(batch);
+ return -EINVAL;
+ }
+ xm2mvsc->batch_size = batch->batch_size;
+ kfree(batch);
+ return 0;
+}
+
+static int xm2mvsc_ioctl_enqueue(struct xm2m_vscale_dev *xm2mvsc,
+ void __user *arg)
+{
+ struct xm2m_vscale_desc *desc;
+ int ret;
+ unsigned long flags;
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+ ret = copy_from_user(&desc->data, arg, sizeof(desc->data));
+ if (ret) {
+ dev_err(xm2mvsc->dev, "%s: Failed to copy from user", __func__);
+ return -EFAULT;
+ }
+ ret = xm2mvsc_verify_desc(desc);
+ if (ret < 0)
+ return ret;
+ /* Assign xm2m_vscale_dev handle */
+ desc->xm2mvsc_dev = xm2mvsc;
+ desc->data.desc_id = atomic_add_return(1, &xm2mvsc->desc_count);
+ desc->src_kaddr = dma_alloc_coherent(xm2mvsc->dev,
+ desc->data.srcbuf_size,
+ &desc->srcbuf_addr,
+ GFP_KERNEL | GFP_DMA32);
+ if (!desc->src_kaddr)
+ return -ENOMEM;
+ desc->dst_kaddr = dma_alloc_coherent(xm2mvsc->dev,
+ desc->data.dstbuf_size,
+ &desc->dstbuf_addr,
+ GFP_KERNEL | GFP_DMA32);
+ if (!desc->dst_kaddr)
+ return -ENOMEM;
+ spin_lock_irqsave(&xm2mvsc->lock, flags);
+ list_add_tail(&desc->node, &xm2mvsc->pending_list);
+ spin_unlock_irqrestore(&xm2mvsc->lock, flags);
+ if (copy_to_user(arg, &desc->data, sizeof(desc->data))) {
+ dev_err(xm2mvsc->dev,
+ "%s : Failed to copy to user for desc_id = %d",
+ __func__, desc->data.desc_id);
+ return -EFAULT;
+ }
+ dev_dbg(xm2mvsc->dev, "%s: Desc_id = %d", __func__, desc->data.desc_id);
+ return 0;
+}
+
+static int xm2mvsc_complete(struct xm2m_vscale_dev *xm2mvsc)
+{
+ struct xm2m_vscale_desc *desc, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&xm2mvsc->lock, flags);
+ list_for_each_entry_safe(desc, next, &xm2mvsc->ongoing_list, node) {
+ list_del(&desc->node);
+ list_add_tail(&desc->node, &xm2mvsc->done_list);
+ atomic_dec(&xm2mvsc->ongoing_count);
+ }
+ spin_unlock_irqrestore(&xm2mvsc->lock, flags);
+ dev_dbg(xm2mvsc->dev, "%s: ongoing_count = %d",
+ __func__, atomic_read(&xm2mvsc->ongoing_count));
+ return 0;
+}
+
+static int xm2mvsc_ready(struct xm2m_vscale_dev *xm2mvsc)
+{
+ unsigned long flags;
+ struct xm2m_vscale_desc *desc, *next;
+
+ spin_lock_irqsave(&xm2mvsc->lock, flags);
+ if (list_empty_careful(&xm2mvsc->pending_list)) {
+ spin_unlock_irqrestore(&xm2mvsc->lock, flags);
+ return -EAGAIN;
+ }
+ if (atomic_read(&xm2mvsc->ongoing_count) < xm2mvsc->batch_size) {
+ list_for_each_entry_safe(desc, next,
+ &xm2mvsc->pending_list, node) {
+ list_del(&desc->node);
+ desc->channel_offset =
+ atomic_read(&xm2mvsc->ongoing_count);
+ WARN(desc->channel_offset > xm2mvsc->hw.max_chan,
+ "%s: Channel offset is beyond supported max",
+ __func__);
+ list_add_tail(&desc->node, &xm2mvsc->ongoing_list);
+ atomic_inc(&xm2mvsc->ongoing_count);
+ dev_dbg(xm2mvsc->dev,
+ "%s: Desc_id=%d offset=%d ongoing count=%d",
+ __func__, desc->data.desc_id,
+ desc->channel_offset,
+ atomic_read(&xm2mvsc->ongoing_count));
+ }
+ }
+ spin_unlock_irqrestore(&xm2mvsc->lock, flags);
+
+ if (atomic_read(&xm2mvsc->ongoing_count) == xm2mvsc->batch_size) {
+ list_for_each_entry_safe(desc, next,
+ &xm2mvsc->ongoing_list, node) {
+ xm2mvsc_write_desc(desc);
+ }
+ dev_dbg(xm2mvsc->dev, "%s: xm2mvsc_start_scaling", __func__);
+ /* Start the IP */
+ xm2mvsc_start_scaling(&xm2mvsc->hw, xm2mvsc->batch_size);
+ }
+ return 0;
+}
+
+/* Can be called from IRQ Handler, not allowed to sleep */
+static int xm2mvsc_start_running(struct xm2m_vscale_dev *xm2mvsc)
+{
+ /* Process and make ready */
+ return xm2mvsc_ready(xm2mvsc);
+}
+
+/*
+ * Implementation may need to change to coalesce
+ * completion of multiple buffers
+ */
+static int xm2mvsc_ioctl_dequeue(struct xm2m_vscale_dev *xm2mvsc,
+ void __user *arg)
+{
+ struct xm2mvsc_dqdata *dqdata;
+ struct xm2m_vscale_desc *desc, *next;
+ unsigned long flags;
+
+ dqdata = kzalloc(sizeof(*dqdata), GFP_KERNEL);
+ if (!dqdata)
+ return -ENOMEM;
+
+ if (copy_from_user(dqdata, arg, sizeof(*dqdata))) {
+ dev_err(xm2mvsc->dev, "%s: Failed to copy from user", __func__);
+ return -EFAULT;
+ }
+
+ /* Underflow or ioctl called too early, try later */
+ spin_lock_irqsave(&xm2mvsc->lock, flags);
+ if (list_empty_careful(&xm2mvsc->done_list)) {
+ spin_unlock_irqrestore(&xm2mvsc->lock, flags);
+ dev_err(xm2mvsc->dev,
+ "%s: failed as done list empty", __func__);
+ return -EAGAIN;
+ }
+ /* Search through the done list, move to free list if found */
+ list_for_each_entry_safe(desc, next, &xm2mvsc->done_list, node) {
+ if (desc->data.desc_id == dqdata->desc_id) {
+ list_del(&desc->node);
+ list_add_tail(&desc->node, &xm2mvsc->free_list);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&xm2mvsc->lock, flags);
+
+ /* Reached end of the list */
+ if (!desc || desc->data.desc_id != dqdata->desc_id) {
+ dev_err(xm2mvsc->dev,
+ "%s: Unable to find desc_id = %d in done list",
+ __func__, dqdata->desc_id);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int xm2mvsc_ioctl_start(struct xm2m_vscale_dev *xm2mvsc)
+{
+ return xm2mvsc_start_running(xm2mvsc);
+}
+
+static void xm2mvsc_free_desc_list(struct list_head *list)
+{
+ struct xm2m_vscale_desc *desc, *next;
+
+ list_for_each_entry_safe(desc, next, list, node) {
+ list_del(&desc->node);
+ kfree(desc);
+ }
+}
+
+/* PS GPIO RESET MACROS */
+#define XM2MVSC_RESET_ASSERT (0x1)
+#define XM2MVSC_RESET_DEASSERT (0x0)
+
+static void xm2mvsc_reset(struct xm2m_vscale_dev *xm2mvsc)
+{
+ gpiod_set_value_cansleep(xm2mvsc->rst_gpio, XM2MVSC_RESET_ASSERT);
+ gpiod_set_value_cansleep(xm2mvsc->rst_gpio, XM2MVSC_RESET_DEASSERT);
+}
+
+static void xm2mvsc_clear_state(struct xm2m_vscale_dev *xm2mvsc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&xm2mvsc->lock, flags);
+ xm2mvsc_free_desc_list(&xm2mvsc->pending_list);
+ xm2mvsc_free_desc_list(&xm2mvsc->ongoing_list);
+ xm2mvsc_free_desc_list(&xm2mvsc->done_list);
+ xm2mvsc_free_desc_list(&xm2mvsc->free_list);
+ spin_unlock_irqrestore(&xm2mvsc->lock, flags);
+
+ spin_lock_irqsave(&xm2mvsc->lock, flags);
+ INIT_LIST_HEAD(&xm2mvsc->pending_list);
+ INIT_LIST_HEAD(&xm2mvsc->ongoing_list);
+ INIT_LIST_HEAD(&xm2mvsc->done_list);
+ INIT_LIST_HEAD(&xm2mvsc->free_list);
+ spin_unlock_irqrestore(&xm2mvsc->lock, flags);
+}
+
+static int xm2mvsc_ioctl_stop(struct xm2m_vscale_dev *xm2mvsc)
+{
+ xm2mvsc_clear_state(xm2mvsc);
+ /* Reset IP */
+ xm2mvsc_stop_scaling(&xm2mvsc->hw);
+ xm2mvsc_reset(xm2mvsc);
+ return 0;
+}
+
+static int xm2mvsc_ioctl_free(struct xm2m_vscale_dev *xm2mvsc,
+ void __user *arg)
+{
+ struct xm2mvsc_dqdata *dqdata;
+ struct xm2m_vscale_desc *desc, *next;
+ int ret;
+
+ dqdata = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!dqdata)
+ return -ENOMEM;
+
+ ret = copy_from_user(dqdata, arg, sizeof(*dqdata));
+ if (ret < 0) {
+ dev_err(xm2mvsc->dev,
+ "%s: Failed to copy from user", __func__);
+ return -EFAULT;
+ }
+
+ list_for_each_entry_safe(desc, next, &xm2mvsc->free_list, node) {
+ if (desc->data.desc_id == dqdata->desc_id) {
+ list_del(&desc->node);
+ break;
+ }
+ }
+
+ if (!desc || desc->data.desc_id != dqdata->desc_id) {
+ dev_err(xm2mvsc->dev,
+ "%s: Desc_id = %d not found in free list",
+ __func__, dqdata->desc_id);
+ kfree(dqdata);
+ return -EBADF;
+ }
+
+ dma_free_coherent(xm2mvsc->dev, desc->data.srcbuf_size,
+ desc->src_kaddr, desc->srcbuf_addr);
+ dma_free_coherent(xm2mvsc->dev, desc->data.dstbuf_size,
+ desc->dst_kaddr, desc->dstbuf_addr);
+ kfree(dqdata);
+ kfree(desc);
+ return 0;
+}
+
+static long xm2mvsc_ioctl(struct file *fptr,
+ unsigned int cmd, unsigned long data)
+{
+ struct xm2m_vscale_dev *xm2mvsc;
+ void __user *arg;
+ int ret;
+
+ xm2mvsc = fptr->private_data;
+ arg = (void __user *)data;
+
+ if (!xm2mvsc || !arg) {
+ pr_err("%s: file op error", __func__);
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case XM2MVSC_ENQUEUE:
+ ret = xm2mvsc_ioctl_enqueue(xm2mvsc, arg);
+ if (ret < 0)
+ return ret;
+ return 0;
+ case XM2MVSC_DEQUEUE:
+ ret = xm2mvsc_ioctl_dequeue(xm2mvsc, arg);
+ if (ret < 0)
+ return ret;
+ return 0;
+ case XM2MVSC_START:
+ ret = xm2mvsc_ioctl_start(xm2mvsc);
+ if (ret < 0)
+ return ret;
+ return 0;
+ case XM2MVSC_STOP:
+ ret = xm2mvsc_ioctl_stop(xm2mvsc);
+ if (ret < 0)
+ return ret;
+ return 0;
+ case XM2MVSC_FREE:
+ ret = xm2mvsc_ioctl_free(xm2mvsc, arg);
+ if (ret < 0)
+ return ret;
+ return 0;
+ case XM2MVSC_BATCH_SIZE:
+ ret = xm2mvsc_ioctl_batch_size(xm2mvsc, arg);
+ if (ret < 0)
+ return ret;
+ return 0;
+ default:
+ dev_err(xm2mvsc->dev, "Unsupported ioctl cmd");
+ return -EINVAL;
+ }
+}
+
+/*
+ * First call maps the source buffer,
+ * second call maps the destination buffer
+ */
+static int xm2mvsc_mmap(struct file *fptr, struct vm_area_struct *vma)
+{
+ struct xm2m_vscale_dev *xm2mvsc = fptr->private_data;
+ struct xm2m_vscale_desc *desc, *next;
+ int ret, desc_id;
+ unsigned long flags;
+
+ if (!xm2mvsc) {
+ pr_err("xm2mvsc file private data is NULL");
+ return -EIO;
+ }
+
+ desc_id = vma->vm_pgoff;
+
+ spin_lock_irqsave(&xm2mvsc->lock, flags);
+ list_for_each_entry_safe(desc, next, &xm2mvsc->pending_list, node) {
+ if (desc->data.desc_id == desc_id)
+ break;
+ }
+ spin_unlock_irqrestore(&xm2mvsc->lock, flags);
+ if (!desc || desc->data.desc_id != desc_id) {
+ dev_err(xm2mvsc->dev,
+ "Unable to find desc_id = %d in pending list",
+ desc_id);
+ return -EIO;
+ }
+ if (!desc->src_kaddr && !desc->dst_kaddr) {
+ dev_err(xm2mvsc->dev, "Enqueue before mmap for desc_id = %d",
+ desc->data.desc_id);
+ }
+ if (desc->data.srcbuf_mmap && desc->data.dstbuf_mmap) {
+ dev_err(xm2mvsc->dev,
+ "Src and Dest buffs already mmap'ed for desc_id = %d",
+ desc->data.desc_id);
+ return -EIO;
+ }
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (!desc->data.srcbuf_mmap) {
+ ret = remap_pfn_range(vma, vma->vm_start,
+ desc->srcbuf_addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+ if (ret) {
+ dev_err(xm2mvsc->dev,
+ "mmap op failed for srcbuf of desc_id = %d",
+ desc->data.desc_id);
+ ret = -EAGAIN;
+ goto error_mmap;
+ }
+ desc->data.srcbuf_mmap = true;
+ goto success_mmap;
+ }
+ if (!desc->data.dstbuf_mmap) {
+ ret = remap_pfn_range(vma, vma->vm_start,
+ desc->dstbuf_addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+ if (ret) {
+ dev_err(xm2mvsc->dev,
+ "mmap op failed for dstbuf of desc_id = %d",
+ desc->data.desc_id);
+ ret = -EAGAIN;
+ goto error_mmap;
+ }
+ desc->data.dstbuf_mmap = true;
+ goto success_mmap;
+ }
+success_mmap:
+ vma->vm_private_data = xm2mvsc;
+ return 0;
+error_mmap:
+ dev_err(xm2mvsc->dev, "%s: failed %d", __func__, ret);
+ return ret;
+}
+
+static unsigned int xm2mvsc_poll(struct file *fptr, poll_table *wait)
+{
+ struct xm2m_vscale_dev *xm2mvsc = fptr->private_data;
+
+ if (!xm2mvsc)
+ return 0;
+
+ poll_wait(fptr, &xm2mvsc->waitq, wait);
+ if (!list_empty_careful(&xm2mvsc->done_list))
+ return POLLIN | POLLPRI;
+ return 0;
+}
+
+static const struct file_operations xm2mvsc_fops = {
+ .open = xm2mvsc_open,
+ .release = xm2mvsc_release,
+ .unlocked_ioctl = xm2mvsc_ioctl,
+ .poll = xm2mvsc_poll,
+ .mmap = xm2mvsc_mmap,
+};
+
+static irqreturn_t xm2mvsc_intr_handler(int irq, void *ctx)
+{
+ u32 status;
+ struct xm2m_vscale_dev *xm2mvsc = (struct xm2m_vscale_dev *)ctx;
+
+ WARN(!xm2mvsc, "%s: xm2mvsc is NULL", __func__);
+ WARN(xm2mvsc->irq != irq,
+ "IRQ registered %d does not match IRQ received %d",
+ xm2mvsc->irq, irq);
+
+ status = xm2mvsc_get_irq_status(&xm2mvsc->hw);
+ if (status) {
+ /* The ongoing descriptors list should be cleared */
+ (void)xm2mvsc_complete(xm2mvsc);
+ wake_up_interruptible(&xm2mvsc->waitq);
+ /* Program next operation if any*/
+ (void)xm2mvsc_start_running(xm2mvsc);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+#define XM2MVSC_OF_TAPS "xlnx,scaler-num-taps"
+#define XM2MVSC_OF_MAX_CHAN "xlnx,scaler-max-chan"
+static int xm2m_vscale_parse_dt_prop(struct xm2m_vscale_dev *xm2mvsc)
+{
+ struct device_node *node;
+ int ret;
+
+ if (!xm2mvsc)
+ return -EIO;
+ node = xm2mvsc->dev->of_node;
+
+ ret = of_property_read_u32(node, XM2MVSC_OF_TAPS,
+ &xm2mvsc->hw.num_taps);
+ if (ret < 0)
+ return ret;
+ switch (xm2mvsc->hw.num_taps) {
+ case XV_SCALER_TAPS_6:
+ case XV_SCALER_TAPS_8:
+ case XV_SCALER_TAPS_10:
+ case XV_SCALER_TAPS_12:
+ break;
+ default:
+ dev_err(xm2mvsc->dev,
+ "Unsupported M2M Scaler taps : %d",
+ xm2mvsc->hw.num_taps);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(node, XM2MVSC_OF_MAX_CHAN,
+ &xm2mvsc->hw.max_chan);
+ if (ret < 0)
+ return ret;
+ if (xm2mvsc->hw.max_chan < XSCALER_BATCH_SIZE_MIN ||
+ xm2mvsc->hw.max_chan > XSCALER_BATCH_SIZE_MAX) {
+ dev_err(xm2mvsc->dev,
+ "Invalid maximum scaler channels : %d",
+ xm2mvsc->hw.max_chan);
+ return -EINVAL;
+ }
+ /* Reset PS GPIO specifier is optional for now */
+ xm2mvsc->rst_gpio = devm_gpiod_get(xm2mvsc->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(xm2mvsc->rst_gpio)) {
+ if (PTR_ERR(xm2mvsc->rst_gpio) != -EPROBE_DEFER) {
+ dev_err(xm2mvsc->dev,
+ "Reset GPIO specifier not setup in DT");
+ }
+ return PTR_ERR(xm2mvsc->rst_gpio);
+ }
+
+ xm2mvsc->irq = irq_of_parse_and_map(node, 0);
+ if (xm2mvsc->irq < 0) {
+ dev_err(xm2mvsc->dev, "Unable to get IRQ");
+ return xm2mvsc->irq;
+ }
+
+ return 0;
+}
+
+static int xm2m_vscale_probe(struct platform_device *pdev)
+{
+ struct xm2m_vscale_dev *xm2mvsc;
+ struct device *dc;
+ struct resource *res;
+ int ret;
+
+ if (atomic_read(&xm2mvsc_ndevs) >= DRIVER_MAX_DEV) {
+ dev_err(&pdev->dev,
+ "Unable to create xm2mvsc devices beyond max %d",
+ DRIVER_MAX_DEV);
+ return -EIO;
+ }
+
+ xm2mvsc = devm_kzalloc(&pdev->dev, sizeof(*xm2mvsc), GFP_KERNEL);
+ if (!xm2mvsc)
+ return -ENOMEM;
+ xm2mvsc->dev = &pdev->dev;
+ xm2mvsc->hw.dev = &pdev->dev;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ xm2mvsc->hw.regs = devm_ioremap_resource(xm2mvsc->dev, res);
+ if (IS_ERR(xm2mvsc->hw.regs))
+ return PTR_ERR(xm2mvsc->hw.regs);
+ ret = xm2m_vscale_parse_dt_prop(xm2mvsc);
+ if (ret < 0)
+ return ret;
+ xm2mvsc_reset(xm2mvsc);
+
+ /* Initialize Scaler Properties */
+ xm2mvsc->hw.max_lines = XM2MVSC_MAX_HEIGHT;
+ xm2mvsc->hw.max_pixels = XM2MVSC_MAX_WIDTH;
+ xm2mvsc_initialize_coeff_banks(&xm2mvsc->hw);
+
+ init_waitqueue_head(&xm2mvsc->waitq);
+ spin_lock_init(&xm2mvsc->lock);
+ INIT_LIST_HEAD(&xm2mvsc->pending_list);
+ INIT_LIST_HEAD(&xm2mvsc->ongoing_list);
+ INIT_LIST_HEAD(&xm2mvsc->done_list);
+ INIT_LIST_HEAD(&xm2mvsc->free_list);
+ ret = devm_request_irq(xm2mvsc->dev, xm2mvsc->irq,
+ xm2mvsc_intr_handler, IRQF_SHARED,
+ DRIVER_NAME, xm2mvsc);
+ if (ret < 0) {
+ dev_err(xm2mvsc->dev, "Unable to register IRQ");
+ return ret;
+ }
+
+ cdev_init(&xm2mvsc->chdev, &xm2mvsc_fops);
+ xm2mvsc->chdev.owner = THIS_MODULE;
+ xm2mvsc->id = atomic_read(&xm2mvsc_ndevs);
+ ret = cdev_add(&xm2mvsc->chdev,
+ MKDEV(MAJOR(xm2mvsc_devt), xm2mvsc->id), 1);
+ if (ret < 0) {
+ dev_err(xm2mvsc->dev, "cdev_add failed");
+ return ret;
+ }
+
+ if (!xm2mvsc_class) {
+ dev_err(xm2mvsc->dev, "xm2mvsc device class not created");
+ goto err_cdev;
+ }
+ dc = device_create(xm2mvsc_class, xm2mvsc->dev,
+ MKDEV(MAJOR(xm2mvsc_devt), xm2mvsc->id),
+ xm2mvsc, "xm2mvsc%d", xm2mvsc->id);
+ if (IS_ERR(dc)) {
+ ret = PTR_ERR(dc);
+ dev_err(xm2mvsc->dev, "Unable to create device");
+ goto err_cdev;
+ }
+ platform_set_drvdata(pdev, xm2mvsc);
+ dev_info(xm2mvsc->dev,
+ "Xilinx M2M Video Scaler %d tap %d channel device probe complete",
+ xm2mvsc->hw.num_taps, xm2mvsc->hw.max_chan);
+ atomic_inc(&xm2mvsc_ndevs);
+ return 0;
+err_cdev:
+ cdev_del(&xm2mvsc->chdev);
+ return ret;
+}
+
+static int xm2m_vscale_remove(struct platform_device *pdev)
+{
+ struct xm2m_vscale_dev *xm2mvsc;
+
+ xm2mvsc = platform_get_drvdata(pdev);
+ if (!xm2mvsc || !xm2mvsc_class)
+ return -EIO;
+ device_destroy(xm2mvsc_class,
+ MKDEV(MAJOR(xm2mvsc_devt), xm2mvsc->id));
+ cdev_del(&xm2mvsc->chdev);
+ atomic_dec(&xm2mvsc_ndevs);
+ return 0;
+}
+
+static const struct of_device_id xm2mvsc_of_match[] = {
+ { .compatible = "xlnx,v-m2m-scaler", },
+ { /* end of table*/ }
+};
+MODULE_DEVICE_TABLE(of, xm2mvsc_of_match);
+
+static struct platform_driver xm2mvsc_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = xm2mvsc_of_match,
+ },
+ .probe = xm2m_vscale_probe,
+ .remove = xm2m_vscale_remove,
+};
+
+static int __init xm2mvsc_init_mod(void)
+{
+ int err;
+
+ xm2mvsc_class = class_create(THIS_MODULE, DRIVER_NAME);
+ if (IS_ERR(xm2mvsc_class)) {
+ pr_err("%s : Unable to create xm2mvsc class", __func__);
+ return PTR_ERR(xm2mvsc_class);
+ }
+ err = alloc_chrdev_region(&xm2mvsc_devt, 0,
+ DRIVER_MAX_DEV, DRIVER_NAME);
+ if (err < 0) {
+ pr_err("%s: Unable to get major number for xm2mvsc", __func__);
+ goto err_class;
+ }
+ err = platform_driver_register(&xm2mvsc_driver);
+ if (err < 0) {
+ pr_err("%s: Unable to register %s driver",
+ __func__, DRIVER_NAME);
+ goto err_pdrv;
+ }
+ return 0;
+err_pdrv:
+ unregister_chrdev_region(xm2mvsc_devt, DRIVER_MAX_DEV);
+err_class:
+ class_destroy(xm2mvsc_class);
+ return err;
+}
+
+static void __exit xm2mvsc_cleanup_mod(void)
+{
+ platform_driver_unregister(&xm2mvsc_driver);
+ unregister_chrdev_region(xm2mvsc_devt, DRIVER_MAX_DEV);
+ class_destroy(xm2mvsc_class);
+ xm2mvsc_class = NULL;
+}
+module_init(xm2mvsc_init_mod);
+module_exit(xm2mvsc_cleanup_mod);
+
+MODULE_AUTHOR("Xilinx Inc.");
+MODULE_DESCRIPTION("Xilinx M2M Video Scaler IP Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/staging/xm2mvscale/xvm2mvsc_hw_regs.h b/drivers/staging/xm2mvscale/xvm2mvsc_hw_regs.h
new file mode 100644
index 0000000..966d3c8
--- /dev/null
+++ b/drivers/staging/xm2mvscale/xvm2mvsc_hw_regs.h
@@ -0,0 +1,204 @@
+// ==============================================================
+// File generated by Vivado(TM) HLS - High-Level Synthesis from
+// C, C++ and SystemC
+// Version: 2018.1.0
+// Copyright (C) 1986-2018 Xilinx, Inc. All Rights Reserved.
+// SPDX-License-Identifier: GPL-2.0
+// ==============================================================
+
+// CTRL
+// 0x0000 : Control signals
+// bit 0 - ap_start (Read/Write/COH)
+// bit 1 - ap_done (Read/COR)
+// bit 2 - ap_idle (Read)
+// bit 3 - ap_ready (Read)
+// bit 7 - auto_restart (Read/Write)
+// others - reserved
+// 0x0004 : Global Interrupt Enable Register
+// bit 0 - Global Interrupt Enable (Read/Write)
+// others - reserved
+// 0x0008 : IP Interrupt Enable Register (Read/Write)
+// bit 0 - Channel 0 (ap_done)
+// bit 1 - Channel 1 (ap_ready)
+// others - reserved
+// 0x000c : IP Interrupt Status Register (Read/TOW)
+// bit 0 - Channel 0 (ap_done)
+// bit 1 - Channel 1 (ap_ready)
+// others - reserved
+// 0x0010 : Data signal of HwReg_num_outs
+// bit 7~0 - HwReg_num_outs[7:0] (Read/Write)
+// others - reserved
+// 0x0014 : reserved
+// 0x0100 : Data signal of HwReg_WidthIn_0
+// bit 15~0 - HwReg_WidthIn_0[15:0] (Read/Write)
+// others - reserved
+// 0x0104 : reserved
+// 0x0108 : Data signal of HwReg_WidthOut_0
+// bit 15~0 - HwReg_WidthOut_0[15:0] (Read/Write)
+// others - reserved
+// 0x010c : reserved
+// 0x0110 : Data signal of HwReg_HeightIn_0
+// bit 15~0 - HwReg_HeightIn_0[15:0] (Read/Write)
+// others - reserved
+// 0x0114 : reserved
+// 0x0118 : Data signal of HwReg_HeightOut_0
+// bit 15~0 - HwReg_HeightOut_0[15:0] (Read/Write)
+// others - reserved
+// 0x011c : reserved
+// 0x0120 : Data signal of HwReg_LineRate_0
+// bit 31~0 - HwReg_LineRate_0[31:0] (Read/Write)
+// 0x0124 : reserved
+// 0x0128 : Data signal of HwReg_PixelRate_0
+// bit 31~0 - HwReg_PixelRate_0[31:0] (Read/Write)
+// 0x012c : reserved
+// 0x0130 : Data signal of HwReg_InPixelFmt_0
+// bit 7~0 - HwReg_InPixelFmt_0[7:0] (Read/Write)
+// others - reserved
+// 0x0134 : reserved
+// 0x0138 : Data signal of HwReg_OutPixelFmt_0
+// bit 7~0 - HwReg_OutPixelFmt_0[7:0] (Read/Write)
+// others - reserved
+// 0x013c : reserved
+// 0x0140 : Data signal of HwReg_FiltIdx_0
+// bit 7~0 - HwReg_FiltIdx_0[7:0] (Read/Write)
+// others - reserved
+// 0x0144 : reserved
+// 0x0150 : Data signal of HwReg_InStride_0
+// bit 15~0 - HwReg_InStride_0[15:0] (Read/Write)
+// others - reserved
+// 0x0154 : reserved
+// 0x0158 : Data signal of HwReg_OutStride_0
+// bit 15~0 - HwReg_OutStride_0[15:0] (Read/Write)
+// others - reserved
+// 0x015c : reserved
+// 0x0160 : Data signal of HwReg_srcImgBuf0_0
+// bit 31~0 - HwReg_srcImgBuf0_0[31:0] (Read/Write)
+// 0x0164 : reserved
+// 0x0170 : Data signal of HwReg_srcImgBuf1_0
+// bit 31~0 - HwReg_srcImgBuf1_0[31:0] (Read/Write)
+// 0x0174 : reserved
+// 0x0190 : Data signal of HwReg_dstImgBuf0_0
+// bit 31~0 - HwReg_dstImgBuf0_0[31:0] (Read/Write)
+// 0x0194 : reserved
+// 0x0200 : Data signal of HwReg_dstImgBuf1_0
+// bit 31~0 - HwReg_dstImgBuf1_0[31:0] (Read/Write)
+// 0x0204 : reserved
+// 0x1500 : Data signal of HwReg_WidthIn_7
+// bit 15~0 - HwReg_WidthIn_7[15:0] (Read/Write)
+// others - reserved
+// 0x1504 : reserved
+// 0x1508 : Data signal of HwReg_WidthOut_7
+// bit 15~0 - HwReg_WidthOut_7[15:0] (Read/Write)
+// others - reserved
+// 0x150c : reserved
+// 0x1510 : Data signal of HwReg_HeightIn_7
+// bit 15~0 - HwReg_HeightIn_7[15:0] (Read/Write)
+// others - reserved
+// 0x1514 : reserved
+// 0x1518 : Data signal of HwReg_HeightOut_7
+// bit 15~0 - HwReg_HeightOut_7[15:0] (Read/Write)
+// others - reserved
+// 0x151c : reserved
+// 0x1520 : Data signal of HwReg_LineRate_7
+// bit 31~0 - HwReg_LineRate_7[31:0] (Read/Write)
+// 0x1524 : reserved
+// 0x1528 : Data signal of HwReg_PixelRate_7
+// bit 31~0 - HwReg_PixelRate_7[31:0] (Read/Write)
+// 0x152c : reserved
+// 0x1530 : Data signal of HwReg_InPixelFmt_7
+// bit 7~0 - HwReg_InPixelFmt_7[7:0] (Read/Write)
+// others - reserved
+// 0x1534 : reserved
+// 0x1538 : Data signal of HwReg_OutPixelFmt_7
+// bit 7~0 - HwReg_OutPixelFmt_7[7:0] (Read/Write)
+// others - reserved
+// 0x153c : reserved
+// 0x1540 : Data signal of HwReg_FiltIdx_7
+// bit 7~0 - HwReg_FiltIdx_7[7:0] (Read/Write)
+// others - reserved
+// 0x1544 : reserved
+// 0x1550 : Data signal of HwReg_InStride_7
+// bit 15~0 - HwReg_InStride_7[15:0] (Read/Write)
+// others - reserved
+// 0x1554 : reserved
+// 0x1558 : Data signal of HwReg_OutStride_7
+// bit 15~0 - HwReg_OutStride_7[15:0] (Read/Write)
+// others - reserved
+// 0x155c : reserved
+// 0x1560 : Data signal of HwReg_srcImgBuf0_7
+// bit 31~0 - HwReg_srcImgBuf0_7[31:0] (Read/Write)
+// 0x1564 : reserved
+// 0x1570 : Data signal of HwReg_srcImgBuf1_7
+// bit 31~0 - HwReg_srcImgBuf1_7[31:0] (Read/Write)
+// 0x1574 : reserved
+// 0x1590 : Data signal of HwReg_dstImgBuf0_7
+// bit 31~0 - HwReg_dstImgBuf0_7[31:0] (Read/Write)
+// 0x1594 : reserved
+// 0x1600 : Data signal of HwReg_dstImgBuf1_7
+// bit 31~0 - HwReg_dstImgBuf1_7[31:0] (Read/Write)
+// 0x1604 : reserved
+// 0x2000 ~
+// 0x23ff : Memory 'HwReg_mm_vfltCoeff_L' (384 * 16b)
+// Word n : bit [15: 0] - HwReg_mm_vfltCoeff_L[2n]
+// bit [31:16] - HwReg_mm_vfltCoeff_L[2n+1]
+// 0x2800 ~
+// 0x2bff : Memory 'HwReg_mm_vfltCoeff_H' (384 * 16b)
+// Word n : bit [15: 0] - HwReg_mm_vfltCoeff_H[2n]
+// bit [31:16] - HwReg_mm_vfltCoeff_H[2n+1]
+// 0x4000 ~
+// 0x43ff : Memory 'HwReg_mm_hfltCoeff_L' (384 * 16b)
+// Word n : bit [15: 0] - HwReg_mm_hfltCoeff_L[2n]
+// bit [31:16] - HwReg_mm_hfltCoeff_L[2n+1]
+// 0x4800 ~
+// 0x4bff : Memory 'HwReg_mm_hfltCoeff_H' (384 * 16b)
+// Word n : bit [15: 0] - HwReg_mm_hfltCoeff_H[2n]
+// bit [31:16] - HwReg_mm_hfltCoeff_H[2n+1]
+// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write,
+// COH = Clear on Handshake)
+#ifndef __HW_REGS_XM2MVSC_H__
+#define __HW_REGS_XM2MVSC_H__
+
+#define XM2MVSC_AP_CTRL 0x0000
+#define XM2MVSC_GIE 0x0004
+#define XM2MVSC_IER 0x0008
+#define XM2MVSC_ISR 0x000c
+#define XM2MVSC_NUM_OUTS 0x0010
+
+#define XM2MVSC_WIDTHIN_0 0x0100
+#define XM2MVSC_WIDTHOUT_0 0x0108
+#define XM2MVSC_HEIGHTIN_0 0x0110
+#define XM2MVSC_HEIGHTOUT_0 0x0118
+#define XM2MVSC_LINERATE_0 0x0120
+#define XM2MVSC_PIXELRATE_0 0x0128
+#define XM2MVSC_INPIXELFMT_0 0x0130
+#define XM2MVSC_OUTPIXELFMT_0 0x0138
+#define XM2MVSC_FILTIDX_0 0x0140
+#define XM2MVSC_INSTRIDE_0 0x0150
+#define XM2MVSC_OUTSTRIDE_0 0x0158
+#define XM2MVSC_SRCIMGBUF0_0 0x0160
+#define XM2MVSC_SRCIMGBUF1_0 0x0170
+#define XM2MVSC_DSTIMGBUF0_0 0x0190
+#define XM2MVSC_DSTIMGBUF1_0 0x0200
+
+#define XM2MVSC_WIDTH_IN(x) (XM2MVSC_WIDTHIN_0 + 0x200 * (x))
+#define XM2MVSC_WIDTH_OUT(x) (XM2MVSC_WIDTHOUT_0 + 0x200 * (x))
+#define XM2MVSC_HEIGHT_IN(x) (XM2MVSC_HEIGHTIN_0 + 0x200 * (x))
+#define XM2MVSC_HEIGHT_OUT(x) (XM2MVSC_HEIGHTOUT_0 + 0x200 * (x))
+#define XM2MVSC_LINERATE(x) (XM2MVSC_LINERATE_0 + 0x200 * (x))
+#define XM2MVSC_PIXELRATE(x) (XM2MVSC_PIXELRATE_0 + 0x200 * (x))
+#define XM2MVSC_PIXELFMT_IN(x) (XM2MVSC_INPIXELFMT_0 + 0x200 * (x))
+#define XM2MVSC_PIXELFMT_OUT(x) (XM2MVSC_OUTPIXELFMT_0 + 0x200 * (x))
+#define XM2MVSC_FILTER_BANK(x) (XM2MVSC_FILTIDX_0 + 0x200 * (x))
+#define XM2MVSC_STRIDE_IN(x) (XM2MVSC_INSTRIDE_0 + 0x200 * (x))
+#define XM2MVSC_STRIDE_OUT(x) (XM2MVSC_OUTSTRIDE_0 + 0x200 * (x))
+#define XM2MVSC_SRC_BUF1(x) (XM2MVSC_SRCIMGBUF0_0 + 0x200 * (x))
+#define XM2MVSC_SRC_BUF2(x) (XM2MVSC_SRCIMGBUF1_0 + 0x200 * (x))
+#define XM2MVSC_DST_BUF1(x) (XM2MVSC_DSTIMGBUF0_0 + 0x200 * (x))
+#define XM2MVSC_DST_BUF2(x) (XM2MVSC_DSTIMGBUF1_0 + 0x200 * (x))
+
+#define XM2MVSC_VFLTCOEFF_L 0x2000
+#define XM2MVSC_VFLTCOEFF(x) (XM2MVSC_VFLTCOEFF_L + 0x800 * (x))
+#define XM2MVSC_HFLTCOEFF_L 0x4000
+#define XM2MVSC_HFLTCOEFF(x) (XM2MVSC_HFLTCOEFF_L + 0x800 * (x))
+
+#endif /* __HW_REGS_XM2MVSC_H__ */
--
1.9.1
More information about the devel
mailing list