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@xxxxxxxxxx> --- 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