diff options
Diffstat (limited to 'drivers/media/platform/xilinx')
34 files changed, 25534 insertions, 250 deletions
diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig index a2773ad7c185..f21796d175a2 100644 --- a/drivers/media/platform/xilinx/Kconfig +++ b/drivers/media/platform/xilinx/Kconfig @@ -4,23 +4,153 @@ config VIDEO_XILINX tristate "Xilinx Video IP (EXPERIMENTAL)" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA select VIDEOBUF2_DMA_CONTIG + select XILINX_FRMBUF select V4L2_FWNODE help Driver for Xilinx Video IP Pipelines if VIDEO_XILINX +config VIDEO_XILINX_AXI4S_SWITCH + tristate "Xilinx AXI4-Stream Video Switch" + depends on VIDEO_XILINX + ---help--- + Driver for the Xilinx AXI4-Stream Video Switch. This is a + V4L sub device based driver. It supports fixed (TDEST based) + as well as dynamic (control register based) routing. + Say M to modularize. Say N if unsure. + +config VIDEO_XILINX_CFA + tristate "Xilinx Video Color Filter Array" + depends on VIDEO_XILINX + ---help--- + Driver for the Xilinx Color Filter Array + +config VIDEO_XILINX_CRESAMPLE + tristate "Xilinx Video Chroma Resampler" + depends on VIDEO_XILINX + ---help--- + Driver for the Xilinx Chroma Resampler + +config VIDEO_XILINX_DEMOSAIC + tristate "Xilinx Video Demosaic IP" + depends on VIDEO_XILINX + ---help--- + Driver for Xilinx Video Demosaic IP. This is a V4L sub-device + based driver for the Demosaic IP that takes input a Bayer video + stream format as input and generates an RGB video output. + Say M to modularize. Say N if unsure. + +config VIDEO_XILINX_GAMMA + tristate "Xilinx Gamma Correction LUT" + depends on VIDEO_XILINX + ---help--- + Driver for Xilinx Gamma Correction LUT IP. This is a V4L sub-device + based driver that exposes V4L controls to adjust Red, Blue and Green + Gamma Correction. + + Say M to modularize. Say N if unsure. + +config VIDEO_XILINX_HLS + tristate "Xilinx Video HLS Core" + depends on VIDEO_XILINX + ---help--- + Driver for the Xilinx Video HLS Cores + +config VIDEO_XILINX_REMAPPER + tristate "Xilinx Video Remapper" + depends on VIDEO_XILINX + ---help--- + Driver for the Xilinx Video Remapper + +config VIDEO_XILINX_RGB2YUV + tristate "Xilinx Video RGB to YUV Convertor" + depends on VIDEO_XILINX + ---help--- + Driver for the Xilinx Video RGB to YUV Convertor + +config VIDEO_XILINX_SCALER + tristate "Xilinx Video Scaler" + depends on VIDEO_XILINX + ---help--- + Driver for the Xilinx Video Scaler + +config VIDEO_XILINX_MULTISCALER + tristate "Xilinx Video Multiscaler" + depends on VIDEO_XILINX + depends on VIDEO_DEV && VIDEO_V4L2 + select V4L2_MEM2MEM_DEV + select VIDEOBUF2_DMA_CONTIG + ---help--- + Driver for the Xilinx Video Multi Scaler. This is a V4L2 memory to + memory based driver. Multi-Scaler has max 8 channels which can be + programed for different scaling ratio. + +config VIDEO_XILINX_SDIRXSS + tristate "Xilinx SDI Rx Subsystem" + depends on VIDEO_XILINX + ---help--- + Driver for Xilinx SDI Rx Subsystem + +config VIDEO_XILINX_SWITCH + tristate "Xilinx Video Switch" + depends on VIDEO_XILINX + ---help--- + Driver for the Xilinx Video Switch + config VIDEO_XILINX_TPG tristate "Xilinx Video Test Pattern Generator" depends on VIDEO_XILINX select VIDEO_XILINX_VTC help - Driver for the Xilinx Video Test Pattern Generator + Driver for the Xilinx Video Test Pattern Generator + +config VIDEO_XILINX_VPSS_CSC + tristate "Xilinx VPSS CSC" + depends on VIDEO_XILINX + ---help--- + Driver for the Xilinx Video Processing Sub-System (VPSS) + Color Space Conversion. The driver provides RGB to YUV444 + conversion and provides video controls like Brightness, + Contrast, Color Gains that can be applied to video. + Say N if unsure. Say M to modularize. + +config VIDEO_XILINX_VPSS_SCALER + tristate "Xilinx Video VPSS Scaler" + depends on VIDEO_XILINX + ---help--- + Driver for Xilinx Video Processing Sub-System(VPSS) Scaler. + It allows upscaling and downscaling of video. It also supports + limited Color Space Conversion. + Say N if unsure. config VIDEO_XILINX_VTC tristate "Xilinx Video Timing Controller" depends on VIDEO_XILINX help - Driver for the Xilinx Video Timing Controller + Driver for the Xilinx Video Timing Controller + +config VIDEO_XILINX_CSI2RXSS + tristate "Xilinx CSI2 Rx Subsystem" + depends on VIDEO_XILINX + ---help--- + Driver for Xilinx MIPI CSI2 Rx Subsystem + +config VIDEO_XILINX_SCD + tristate "Xilinx Scene Change Detect" + depends on VIDEO_XILINX + ---help--- + Driver for Xilinx Scene Change Detection Controller. + The driver allows applications to pass video buffers and + provides if scene change detection is present between + adjacent frames. + +config VIDEO_XILINX_M2M + tristate "Xilinx Video mem2mem" + depends on VIDEO_XILINX + select V4L2_MEM2MEM_DEV + ---help--- + Driver for Xilinx V4L2 mem2mem pipeline operation to achieve memory + copy between two different physical memories using DMA transfers. endif #VIDEO_XILINX diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile index 4cdc0b1ec7a5..f7838c307405 100644 --- a/drivers/media/platform/xilinx/Makefile +++ b/drivers/media/platform/xilinx/Makefile @@ -1,7 +1,26 @@ # SPDX-License-Identifier: GPL-2.0 xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o +xilinx-scd-objs += xilinx-scenechange.o xilinx-scenechange-channel.o \ + xilinx-scenechange-dma.o obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o +obj-$(CONFIG_VIDEO_XILINX_AXI4S_SWITCH) += xilinx-axis-switch.o +obj-$(CONFIG_VIDEO_XILINX_CFA) += xilinx-cfa.o +obj-$(CONFIG_VIDEO_XILINX_CRESAMPLE) += xilinx-cresample.o +obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o +obj-$(CONFIG_VIDEO_XILINX_DEMOSAIC) += xilinx-demosaic.o +obj-$(CONFIG_VIDEO_XILINX_GAMMA) += xilinx-gamma.o +obj-$(CONFIG_VIDEO_XILINX_HLS) += xilinx-hls.o +obj-$(CONFIG_VIDEO_XILINX_M2M) += xilinx-m2m.o +obj-$(CONFIG_VIDEO_XILINX_MULTISCALER) += xilinx-multi-scaler.o +obj-$(CONFIG_VIDEO_XILINX_REMAPPER) += xilinx-remapper.o +obj-$(CONFIG_VIDEO_XILINX_RGB2YUV) += xilinx-rgb2yuv.o +obj-$(CONFIG_VIDEO_XILINX_SCALER) += xilinx-scaler.o +obj-$(CONFIG_VIDEO_XILINX_SCD) += xilinx-scd.o +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o +obj-$(CONFIG_VIDEO_XILINX_SWITCH) += xilinx-switch.o obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o +obj-$(CONFIG_VIDEO_XILINX_VPSS_CSC) += xilinx-vpss-csc.o +obj-$(CONFIG_VIDEO_XILINX_VPSS_SCALER) += xilinx-vpss-scaler.o obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o diff --git a/drivers/media/platform/xilinx/xilinx-axis-switch.c b/drivers/media/platform/xilinx/xilinx-axis-switch.c new file mode 100644 index 000000000000..3963e364570a --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-axis-switch.c @@ -0,0 +1,588 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx AXI4-Stream Video Switch + * + * Copyright (C) 2018 Xilinx, Inc. + * + * Author: Vishal Sagar <vishal.sagar@xilinx.com> + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <media/media-device.h> +#include <media/v4l2-async.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-vip.h" + +#define XVSW_CTRL_REG 0x00 +#define XVSW_CTRL_REG_UPDATE_MASK BIT(1) + +#define XVSW_MI_MUX_REG_BASE 0x40 +#define XVSW_MI_MUX_VAL_MASK 0xF +#define XVSW_MI_MUX_DISABLE_MASK BIT(31) + +#define MIN_VSW_SINKS 1 +#define MAX_VSW_SINKS 16 +#define MIN_VSW_SRCS 1 +#define MAX_VSW_SRCS 16 + +/** + * struct xvswitch_device - Xilinx AXI4-Stream Switch device structure + * @dev: Platform structure + * @iomem: Base address of IP + * @subdev: The v4l2 subdev structure + * @pads: media pads + * @routing: sink pad connected to each source pad (-1 if none) + * @formats: active V4L2 media bus formats on sink pads + * @nsinks: number of sink pads (1 to 8) + * @nsources: number of source pads (2 to 8) + * @tdest_routing: Whether TDEST routing is enabled + * @aclk: Video clock + * @saxi_ctlclk: AXI-Lite control clock + */ +struct xvswitch_device { + struct device *dev; + void __iomem *iomem; + struct v4l2_subdev subdev; + struct media_pad *pads; + int routing[MAX_VSW_SRCS]; + struct v4l2_mbus_framefmt *formats; + u32 nsinks; + u32 nsources; + bool tdest_routing; + struct clk *aclk; + struct clk *saxi_ctlclk; +}; + +static inline struct xvswitch_device *to_xvsw(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xvswitch_device, subdev); +} + +static inline u32 xvswitch_read(struct xvswitch_device *xvsw, u32 addr) +{ + return ioread32(xvsw->iomem + addr); +} + +static inline void xvswitch_write(struct xvswitch_device *xvsw, u32 addr, + u32 value) +{ + iowrite32(value, xvsw->iomem + addr); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Video Operations + */ + +static int xvsw_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xvswitch_device *xvsw = to_xvsw(subdev); + unsigned int i; + + /* Nothing to be done in case of TDEST routing */ + if (xvsw->tdest_routing) + return 0; + + if (!enable) { + /* In control reg routing, disable all master ports */ + for (i = 0; i < xvsw->nsources; i++) { + xvswitch_write(xvsw, XVSW_MI_MUX_REG_BASE + (i * 4), + XVSW_MI_MUX_DISABLE_MASK); + } + xvswitch_write(xvsw, XVSW_CTRL_REG, XVSW_CTRL_REG_UPDATE_MASK); + return 0; + } + + /* + * In case of control reg routing, + * from routing table write the values into respective reg + * and enable + */ + for (i = 0; i < MAX_VSW_SRCS; i++) { + u32 val; + + if (xvsw->routing[i] != -1) + val = xvsw->routing[i]; + else + val = XVSW_MI_MUX_DISABLE_MASK; + + xvswitch_write(xvsw, XVSW_MI_MUX_REG_BASE + (i * 4), + val); + } + + xvswitch_write(xvsw, XVSW_CTRL_REG, XVSW_CTRL_REG_UPDATE_MASK); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static struct v4l2_mbus_framefmt * +xvsw_get_pad_format(struct xvswitch_device *xvsw, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xvsw->subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xvsw->formats[pad]; + default: + return NULL; + } +} + +static int xvsw_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xvswitch_device *xvsw = to_xvsw(subdev); + int pad = fmt->pad; + + /* + * If control reg routing and pad is source pad then + * get corresponding sink pad. if no sink pad then + * clear the format and return + */ + + if (!xvsw->tdest_routing && pad >= xvsw->nsinks) { + pad = xvsw->routing[pad - xvsw->nsinks]; + if (pad < 0) { + memset(&fmt->format, 0, sizeof(fmt->format)); + return 0; + } + } + + fmt->format = *xvsw_get_pad_format(xvsw, cfg, pad, fmt->which); + + return 0; +} + +static int xvsw_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xvswitch_device *xvsw = to_xvsw(subdev); + struct v4l2_mbus_framefmt *format; + + if (!xvsw->tdest_routing && fmt->pad >= xvsw->nsinks) { + /* + * In case of control reg routing, + * get the corresponding sink pad to source pad passed. + * + * The source pad format is always identical to the + * sink pad format and can't be modified. + * + * If sink pad found then get_format for that pad + * else clear the fmt->format as the source pad + * isn't connected and return. + */ + return xvsw_get_format(subdev, cfg, fmt); + } + + /* + * In TDEST routing mode, one can set any format on the pad as + * it can't be checked which pad's data will travel to + * which pad. E.g. In a system with 2 slaves and 4 masters, + * S0 or S1 data can reach M0 thru M3 based on TDEST + * S0 may have RBG and S1 may have YUV. M0, M1 stream RBG + * and M2, M3 stream YUV based on TDEST. + * + * In Control reg routing mode, set format only for sink pads. + */ + format = xvsw_get_pad_format(xvsw, cfg, fmt->pad, fmt->which); + + format->code = fmt->format.code; + format->width = clamp_t(unsigned int, fmt->format.width, + XVIP_MIN_WIDTH, XVIP_MAX_WIDTH); + format->height = clamp_t(unsigned int, fmt->format.height, + XVIP_MIN_HEIGHT, XVIP_MAX_HEIGHT); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + return 0; +} + +static int xvsw_get_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_routing *route) +{ + struct xvswitch_device *xvsw = to_xvsw(subdev); + unsigned int i; + u32 min; + + /* In case of tdest routing, we can't get routing */ + if (xvsw->tdest_routing) + return -EINVAL; + + mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex); + + if (xvsw->nsources < route->num_routes) + min = xvsw->nsources; + else + min = route->num_routes; + + for (i = 0; i < min; ++i) { + route->routes[i].sink = xvsw->routing[i]; + route->routes[i].source = i; + } + + route->num_routes = xvsw->nsources; + + mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex); + + return 0; +} + +static int xvsw_set_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_routing *route) +{ + struct xvswitch_device *xvsw = to_xvsw(subdev); + unsigned int i; + int ret = 0; + + /* In case of tdest routing, we can't set routing */ + if (xvsw->tdest_routing) + return -EINVAL; + + mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex); + + if (subdev->entity.stream_count) { + ret = -EBUSY; + goto done; + } + + for (i = 0; i < xvsw->nsources; ++i) + xvsw->routing[i] = -1; + + for (i = 0; i < route->num_routes; ++i) + xvsw->routing[route->routes[i].source - xvsw->nsinks] = + route->routes[i].sink; + +done: + mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex); + return ret; +} + +static int xvsw_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static int xvsw_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static struct v4l2_subdev_video_ops xvsw_video_ops = { + .s_stream = xvsw_s_stream, +}; + +static struct v4l2_subdev_pad_ops xvsw_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xvip_enum_frame_size, + .get_fmt = xvsw_get_format, + .set_fmt = xvsw_set_format, + .get_routing = xvsw_get_routing, + .set_routing = xvsw_set_routing, +}; + +static struct v4l2_subdev_ops xvsw_ops = { + .video = &xvsw_video_ops, + .pad = &xvsw_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xvsw_internal_ops = { + .open = xvsw_open, + .close = xvsw_close, +}; + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static bool xvsw_has_route(struct media_entity *entity, unsigned int pad0, + unsigned int pad1) +{ + struct xvswitch_device *xvsw = + container_of(entity, struct xvswitch_device, subdev.entity); + unsigned int sink0, sink1; + + /* Two sinks are never connected together. */ + if (pad0 < xvsw->nsinks && pad1 < xvsw->nsinks) + return false; + + /* In TDEST routing, assume all sinks and sources are connected */ + if (xvsw->tdest_routing) + return true; + + sink0 = pad0 < xvsw->nsinks ? pad0 : xvsw->routing[pad0 - xvsw->nsinks]; + sink1 = pad1 < xvsw->nsinks ? pad1 : xvsw->routing[pad1 - xvsw->nsinks]; + + return sink0 == sink1; +} + +static const struct media_entity_operations xvsw_media_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_route = xvsw_has_route, +}; + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static int xvsw_parse_of(struct xvswitch_device *xvsw) +{ + struct device_node *node = xvsw->dev->of_node; + struct device_node *ports; + struct device_node *port; + unsigned int nports = 0; + u32 routing_mode; + int ret; + + ret = of_property_read_u32(node, "xlnx,num-si-slots", &xvsw->nsinks); + if (ret < 0 || xvsw->nsinks < MIN_VSW_SINKS || + xvsw->nsinks > MAX_VSW_SINKS) { + dev_err(xvsw->dev, "missing or invalid xlnx,num-si-slots property\n"); + return ret; + } + + ret = of_property_read_u32(node, "xlnx,num-mi-slots", &xvsw->nsources); + if (ret < 0 || xvsw->nsources < MIN_VSW_SRCS || + xvsw->nsources > MAX_VSW_SRCS) { + dev_err(xvsw->dev, "missing or invalid xlnx,num-mi-slots property\n"); + return ret; + } + + ret = of_property_read_u32(node, "xlnx,routing-mode", &routing_mode); + if (ret < 0 || routing_mode < 0 || routing_mode > 1) { + dev_err(xvsw->dev, "missing or invalid xlnx,routing property\n"); + return ret; + } + + if (!routing_mode) + xvsw->tdest_routing = true; + + xvsw->aclk = devm_clk_get(xvsw->dev, "aclk"); + if (IS_ERR(xvsw->aclk)) { + ret = PTR_ERR(xvsw->aclk); + dev_err(xvsw->dev, "failed to get ap_clk (%d)\n", ret); + return ret; + } + + if (!xvsw->tdest_routing) { + xvsw->saxi_ctlclk = devm_clk_get(xvsw->dev, + "s_axi_ctl_clk"); + if (IS_ERR(xvsw->saxi_ctlclk)) { + ret = PTR_ERR(xvsw->saxi_ctlclk); + dev_err(xvsw->dev, + "failed to get s_axi_ctl_clk (%d)\n", + ret); + return ret; + } + } + + if (xvsw->tdest_routing && xvsw->nsinks > 1) { + dev_err(xvsw->dev, "sinks = %d. Driver Limitation max 1 sink in TDEST routing mode\n", + xvsw->nsinks); + return -EINVAL; + } + + ports = of_get_child_by_name(node, "ports"); + if (!ports) + ports = node; + + for_each_child_of_node(ports, port) { + struct device_node *endpoint; + + if (!port->name || of_node_cmp(port->name, "port")) + continue; + + endpoint = of_get_next_child(port, NULL); + if (!endpoint) { + dev_err(xvsw->dev, "No port at\n"); + return -EINVAL; + } + + /* Count the number of ports. */ + nports++; + } + + /* validate number of ports */ + if (nports != (xvsw->nsinks + xvsw->nsources)) { + dev_err(xvsw->dev, "invalid number of ports %u\n", nports); + return -EINVAL; + } + + return 0; +} + +static int xvsw_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct xvswitch_device *xvsw; + struct resource *res; + unsigned int npads; + unsigned int i, padcount; + int ret; + + xvsw = devm_kzalloc(&pdev->dev, sizeof(*xvsw), GFP_KERNEL); + if (!xvsw) + return -ENOMEM; + + xvsw->dev = &pdev->dev; + + ret = xvsw_parse_of(xvsw); + if (ret < 0) + return ret; + + /* ioremap only if control reg based routing */ + if (!xvsw->tdest_routing) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xvsw->iomem = devm_ioremap_resource(xvsw->dev, res); + if (IS_ERR(xvsw->iomem)) + return PTR_ERR(xvsw->iomem); + } + + /* + * Initialize V4L2 subdevice and media entity. Pad numbers depend on the + * number of pads. + */ + npads = xvsw->nsinks + xvsw->nsources; + xvsw->pads = devm_kzalloc(&pdev->dev, npads * sizeof(*xvsw->pads), + GFP_KERNEL); + if (!xvsw->pads) + return -ENOMEM; + + for (i = 0; i < xvsw->nsinks; ++i) + xvsw->pads[i].flags = MEDIA_PAD_FL_SINK; + + for (; i < npads; ++i) + xvsw->pads[i].flags = MEDIA_PAD_FL_SOURCE; + + padcount = xvsw->tdest_routing ? npads : xvsw->nsinks; + + /* + * In case of tdest routing, allocate format per pad. + * source pad format has to match one of the sink pads in tdest routing. + * + * Otherwise only allocate for sinks as sources will + * get the same pad format and corresponding sink. + * set format on src pad will return corresponding sinks data. + */ + xvsw->formats = devm_kzalloc(&pdev->dev, + padcount * sizeof(*xvsw->formats), + GFP_KERNEL); + if (!xvsw->formats) { + dev_err(xvsw->dev, "No memory to allocate formats!\n"); + return -ENOMEM; + } + + for (i = 0; i < padcount; i++) { + xvsw->formats[i].code = MEDIA_BUS_FMT_RGB888_1X24; + xvsw->formats[i].field = V4L2_FIELD_NONE; + xvsw->formats[i].colorspace = V4L2_COLORSPACE_SRGB; + xvsw->formats[i].width = XVIP_MAX_WIDTH; + xvsw->formats[i].height = XVIP_MAX_HEIGHT; + } + + /* + * Initialize the routing table if none are connected. + * Routing table is valid only incase routing is not TDEST based. + */ + for (i = 0; i < MAX_VSW_SRCS; ++i) + xvsw->routing[i] = -1; + + ret = clk_prepare_enable(xvsw->aclk); + if (ret) { + dev_err(&pdev->dev, "failed to enable aclk (%d)\n", + ret); + return ret; + } + + if (!xvsw->tdest_routing) { + ret = clk_prepare_enable(xvsw->saxi_ctlclk); + if (ret) { + dev_err(&pdev->dev, + "failed to enable s_axi_ctl_clk (%d)\n", + ret); + clk_disable_unprepare(xvsw->aclk); + return ret; + } + } + + subdev = &xvsw->subdev; + v4l2_subdev_init(subdev, &xvsw_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xvsw_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xvsw); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->entity.ops = &xvsw_media_ops; + + ret = media_entity_pads_init(&subdev->entity, npads, xvsw->pads); + if (ret < 0) + goto clk_error; + + platform_set_drvdata(pdev, xvsw); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + dev_info(xvsw->dev, "Xilinx AXI4-Stream Switch found!\n"); + + return 0; + +error: + media_entity_cleanup(&subdev->entity); +clk_error: + if (!xvsw->tdest_routing) + clk_disable_unprepare(xvsw->saxi_ctlclk); + clk_disable_unprepare(xvsw->aclk); + return ret; +} + +static int xvsw_remove(struct platform_device *pdev) +{ + struct xvswitch_device *xvsw = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xvsw->subdev; + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + if (!xvsw->tdest_routing) + clk_disable_unprepare(xvsw->saxi_ctlclk); + clk_disable_unprepare(xvsw->aclk); + return 0; +} + +static const struct of_device_id xvsw_of_id_table[] = { + { .compatible = "xlnx,axis-switch-1.1" }, + { } +}; +MODULE_DEVICE_TABLE(of, xvsw_of_id_table); + +static struct platform_driver xvsw_driver = { + .driver = { + .name = "xilinx-axis-switch", + .of_match_table = xvsw_of_id_table, + }, + .probe = xvsw_probe, + .remove = xvsw_remove, +}; + +module_platform_driver(xvsw_driver); + +MODULE_AUTHOR("Vishal Sagar <vishal.sagar@xilinx.com>"); +MODULE_DESCRIPTION("Xilinx AXI4-Stream Switch Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-cfa.c b/drivers/media/platform/xilinx/xilinx-cfa.c new file mode 100644 index 000000000000..832fb7306563 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-cfa.c @@ -0,0 +1,394 @@ +/* + * Xilinx Color Filter Array + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-vip.h" + +#define XCFA_BAYER_PHASE 0x100 +#define XCFA_BAYER_PHASE_RGGB 0 +#define XCFA_BAYER_PHASE_GRBG 1 +#define XCFA_BAYER_PHASE_GBRG 2 +#define XCFA_BAYER_PHASE_BGGR 3 + +/** + * struct xcfa_device - Xilinx CFA device structure + * @xvip: Xilinx Video IP device + * @pads: media pads + * @formats: V4L2 media bus formats + * @default_formats: default V4L2 media bus formats + * @vip_formats: Xilinx Video IP formats + */ +struct xcfa_device { + struct xvip_device xvip; + + struct media_pad pads[2]; + + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct xvip_video_format *vip_formats[2]; +}; + +static inline struct xcfa_device *to_cfa(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xcfa_device, xvip.subdev); +} + +/* + * V4L2 Subdevice Video Operations + */ + +static int xcfa_get_bayer_phase(const unsigned int code) +{ + switch (code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + return XCFA_BAYER_PHASE_RGGB; + case MEDIA_BUS_FMT_SGRBG8_1X8: + return XCFA_BAYER_PHASE_GRBG; + case MEDIA_BUS_FMT_SGBRG8_1X8: + return XCFA_BAYER_PHASE_GBRG; + case MEDIA_BUS_FMT_SBGGR8_1X8: + return XCFA_BAYER_PHASE_BGGR; + } + + return -EINVAL; +} + +static int xcfa_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xcfa_device *xcfa = to_cfa(subdev); + const unsigned int code = xcfa->formats[XVIP_PAD_SINK].code; + u32 bayer_phase; + + if (!enable) { + xvip_stop(&xcfa->xvip); + return 0; + } + + /* This always returns the valid bayer phase value */ + bayer_phase = xcfa_get_bayer_phase(code); + + xvip_write(&xcfa->xvip, XCFA_BAYER_PHASE, bayer_phase); + + xvip_set_frame_size(&xcfa->xvip, &xcfa->formats[XVIP_PAD_SINK]); + + xvip_start(&xcfa->xvip); + + return 0; +} + +/* + * V4L2 Subdevice Pad Operations + */ + +static struct v4l2_mbus_framefmt * +__xcfa_get_pad_format(struct xcfa_device *xcfa, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xcfa->xvip.subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xcfa->formats[pad]; + default: + return NULL; + } +} + +static int xcfa_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcfa_device *xcfa = to_cfa(subdev); + + fmt->format = *__xcfa_get_pad_format(xcfa, cfg, fmt->pad, fmt->which); + + return 0; +} + +static int xcfa_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcfa_device *xcfa = to_cfa(subdev); + struct v4l2_mbus_framefmt *format; + int bayer_phase; + + format = __xcfa_get_pad_format(xcfa, cfg, fmt->pad, fmt->which); + + if (fmt->pad == XVIP_PAD_SOURCE) { + fmt->format = *format; + return 0; + } + + bayer_phase = xcfa_get_bayer_phase(fmt->format.code); + if (bayer_phase >= 0) { + xcfa->vip_formats[XVIP_PAD_SINK] = + xvip_get_format_by_code(fmt->format.code); + format->code = fmt->format.code; + } + + xvip_set_format_size(format, fmt); + + fmt->format = *format; + + /* Propagate the format to the source pad */ + format = __xcfa_get_pad_format(xcfa, cfg, XVIP_PAD_SOURCE, fmt->which); + + xvip_set_format_size(format, fmt); + + return 0; +} + +/* + * V4L2 Subdevice Operations + */ + +static int xcfa_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct xcfa_device *xcfa = to_cfa(subdev); + struct v4l2_mbus_framefmt *format; + + /* Initialize with default formats */ + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK); + *format = xcfa->default_formats[XVIP_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE); + *format = xcfa->default_formats[XVIP_PAD_SOURCE]; + + return 0; +} + +static int xcfa_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static struct v4l2_subdev_video_ops xcfa_video_ops = { + .s_stream = xcfa_s_stream, +}; + +static struct v4l2_subdev_pad_ops xcfa_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xvip_enum_frame_size, + .get_fmt = xcfa_get_format, + .set_fmt = xcfa_set_format, +}; + +static struct v4l2_subdev_ops xcfa_ops = { + .video = &xcfa_video_ops, + .pad = &xcfa_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xcfa_internal_ops = { + .open = xcfa_open, + .close = xcfa_close, +}; + +/* + * Media Operations + */ + +static const struct media_entity_operations xcfa_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * Power Management + */ + +static int __maybe_unused xcfa_pm_suspend(struct device *dev) +{ + struct xcfa_device *xcfa = dev_get_drvdata(dev); + + xvip_suspend(&xcfa->xvip); + + return 0; +} + +static int __maybe_unused xcfa_pm_resume(struct device *dev) +{ + struct xcfa_device *xcfa = dev_get_drvdata(dev); + + xvip_resume(&xcfa->xvip); + + return 0; +} + +/* + * Platform Device Driver + */ + +static int xcfa_parse_of(struct xcfa_device *xcfa) +{ + struct device *dev = xcfa->xvip.dev; + struct device_node *node = xcfa->xvip.dev->of_node; + struct device_node *ports; + struct device_node *port; + u32 port_id; + int ret; + + ports = of_get_child_by_name(node, "ports"); + if (ports == NULL) + ports = node; + + /* Get the format description for each pad */ + for_each_child_of_node(ports, port) { + if (port->name && (of_node_cmp(port->name, "port") == 0)) { + const struct xvip_video_format *vip_format; + + vip_format = xvip_of_get_format(port); + if (IS_ERR(vip_format)) { + dev_err(dev, "invalid format in DT"); + return PTR_ERR(vip_format); + } + + ret = of_property_read_u32(port, "reg", &port_id); + if (ret < 0) { + dev_err(dev, "no reg in DT"); + return ret; + } + + if (port_id != 0 && port_id != 1) { + dev_err(dev, "invalid reg in DT"); + return -EINVAL; + } + + xcfa->vip_formats[port_id] = vip_format; + } + } + + return 0; +} + +static int xcfa_probe(struct platform_device *pdev) +{ + struct xcfa_device *xcfa; + struct v4l2_subdev *subdev; + struct v4l2_mbus_framefmt *default_format; + int ret; + + xcfa = devm_kzalloc(&pdev->dev, sizeof(*xcfa), GFP_KERNEL); + if (!xcfa) + return -ENOMEM; + + xcfa->xvip.dev = &pdev->dev; + + ret = xcfa_parse_of(xcfa); + if (ret < 0) + return ret; + + ret = xvip_init_resources(&xcfa->xvip); + if (ret < 0) + return ret; + + /* Reset and initialize the core */ + xvip_reset(&xcfa->xvip); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xcfa->xvip.subdev; + v4l2_subdev_init(subdev, &xcfa_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xcfa_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xcfa); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Initialize default and active formats */ + default_format = &xcfa->default_formats[XVIP_PAD_SINK]; + default_format->code = xcfa->vip_formats[XVIP_PAD_SINK]->code; + default_format->field = V4L2_FIELD_NONE; + default_format->colorspace = V4L2_COLORSPACE_SRGB; + xvip_get_frame_size(&xcfa->xvip, default_format); + + xcfa->formats[XVIP_PAD_SINK] = *default_format; + + default_format = &xcfa->default_formats[XVIP_PAD_SOURCE]; + *default_format = xcfa->default_formats[XVIP_PAD_SINK]; + default_format->code = xcfa->vip_formats[XVIP_PAD_SOURCE]->code; + + xcfa->formats[XVIP_PAD_SOURCE] = *default_format; + + xcfa->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xcfa->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &xcfa_media_ops; + ret = media_entity_pads_init(&subdev->entity, 2, xcfa->pads); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, xcfa); + + xvip_print_version(&xcfa->xvip); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + return 0; + +error: + media_entity_cleanup(&subdev->entity); + xvip_cleanup_resources(&xcfa->xvip); + return ret; +} + +static int xcfa_remove(struct platform_device *pdev) +{ + struct xcfa_device *xcfa = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xcfa->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + + xvip_cleanup_resources(&xcfa->xvip); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xcfa_pm_ops, xcfa_pm_suspend, xcfa_pm_resume); + +static const struct of_device_id xcfa_of_id_table[] = { + { .compatible = "xlnx,v-cfa-7.0" }, + { } +}; +MODULE_DEVICE_TABLE(of, xcfa_of_id_table); + +static struct platform_driver xcfa_driver = { + .driver = { + .name = "xilinx-cfa", + .pm = &xcfa_pm_ops, + .of_match_table = xcfa_of_id_table, + }, + .probe = xcfa_probe, + .remove = xcfa_remove, +}; + +module_platform_driver(xcfa_driver); + +MODULE_DESCRIPTION("Xilinx Color Filter Array Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-cresample.c b/drivers/media/platform/xilinx/xilinx-cresample.c new file mode 100644 index 000000000000..05335c10a388 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-cresample.c @@ -0,0 +1,447 @@ +/* + * Xilinx Chroma Resampler + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/xilinx-v4l2-controls.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-vip.h" + +#define XCRESAMPLE_ENCODING 0x100 +#define XCRESAMPLE_ENCODING_FIELD (1 << 7) +#define XCRESAMPLE_ENCODING_CHROMA (1 << 8) + +/** + * struct xcresample_device - Xilinx CRESAMPLE device structure + * @xvip: Xilinx Video IP device + * @pads: media pads + * @formats: V4L2 media bus formats at the sink and source pads + * @default_formats: default V4L2 media bus formats + * @vip_formats: Xilinx Video IP formats + * @ctrl_handler: control handler + */ +struct xcresample_device { + struct xvip_device xvip; + + struct media_pad pads[2]; + + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct xvip_video_format *vip_formats[2]; + + struct v4l2_ctrl_handler ctrl_handler; +}; + +static inline struct xcresample_device *to_cresample(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xcresample_device, xvip.subdev); +} + +/* + * V4L2 Subdevice Video Operations + */ + +static int xcresample_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xcresample_device *xcresample = to_cresample(subdev); + + if (!enable) { + xvip_stop(&xcresample->xvip); + return 0; + } + + xvip_set_frame_size(&xcresample->xvip, + &xcresample->formats[XVIP_PAD_SINK]); + + xvip_start(&xcresample->xvip); + + return 0; +} + +/* + * V4L2 Subdevice Pad Operations + */ + +static struct v4l2_mbus_framefmt * +__xcresample_get_pad_format(struct xcresample_device *xcresample, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xcresample->xvip.subdev, cfg, + pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xcresample->formats[pad]; + default: + return NULL; + } +} + +static int xcresample_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcresample_device *xcresample = to_cresample(subdev); + + fmt->format = *__xcresample_get_pad_format(xcresample, cfg, fmt->pad, + fmt->which); + + return 0; +} + +static int xcresample_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcresample_device *xcresample = to_cresample(subdev); + struct v4l2_mbus_framefmt *format; + + format = __xcresample_get_pad_format(xcresample, cfg, fmt->pad, + fmt->which); + + if (fmt->pad == XVIP_PAD_SOURCE) { + fmt->format = *format; + return 0; + } + + xvip_set_format_size(format, fmt); + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = __xcresample_get_pad_format(xcresample, cfg, XVIP_PAD_SOURCE, + fmt->which); + + xvip_set_format_size(format, fmt); + + return 0; +} + +/* + * V4L2 Subdevice Operations + */ + +static int xcresample_open(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + struct xcresample_device *xcresample = to_cresample(subdev); + struct v4l2_mbus_framefmt *format; + + /* Initialize with default formats */ + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK); + *format = xcresample->default_formats[XVIP_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE); + *format = xcresample->default_formats[XVIP_PAD_SOURCE]; + + return 0; +} + +static int xcresample_close(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static int xcresample_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct xcresample_device *xcresample = + container_of(ctrl->handler, struct xcresample_device, + ctrl_handler); + switch (ctrl->id) { + case V4L2_CID_XILINX_CRESAMPLE_FIELD_PARITY: + xvip_clr_or_set(&xcresample->xvip, XCRESAMPLE_ENCODING, + XCRESAMPLE_ENCODING_FIELD, ctrl->val); + return 0; + case V4L2_CID_XILINX_CRESAMPLE_CHROMA_PARITY: + xvip_clr_or_set(&xcresample->xvip, XCRESAMPLE_ENCODING, + XCRESAMPLE_ENCODING_CHROMA, ctrl->val); + return 0; + } + + return -EINVAL; + +} + +static const struct v4l2_ctrl_ops xcresample_ctrl_ops = { + .s_ctrl = xcresample_s_ctrl, +}; + +static struct v4l2_subdev_video_ops xcresample_video_ops = { + .s_stream = xcresample_s_stream, +}; + +static struct v4l2_subdev_pad_ops xcresample_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xvip_enum_frame_size, + .get_fmt = xcresample_get_format, + .set_fmt = xcresample_set_format, +}; + +static struct v4l2_subdev_ops xcresample_ops = { + .video = &xcresample_video_ops, + .pad = &xcresample_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xcresample_internal_ops = { + .open = xcresample_open, + .close = xcresample_close, +}; + +/* + * Control Configs + */ + +static const char *const xcresample_parity_string[] = { + "Even", + "Odd", +}; + +static struct v4l2_ctrl_config xcresample_field = { + .ops = &xcresample_ctrl_ops, + .id = V4L2_CID_XILINX_CRESAMPLE_FIELD_PARITY, + .name = "Chroma Resampler: Encoding Field Parity", + .type = V4L2_CTRL_TYPE_MENU, + .min = 0, + .max = 1, + .qmenu = xcresample_parity_string, +}; + +static struct v4l2_ctrl_config xcresample_chroma = { + .ops = &xcresample_ctrl_ops, + .id = V4L2_CID_XILINX_CRESAMPLE_CHROMA_PARITY, + .name = "Chroma Resampler: Encoding Chroma Parity", + .type = V4L2_CTRL_TYPE_MENU, + .min = 0, + .max = 1, + .qmenu = xcresample_parity_string, +}; + +/* + * Media Operations + */ + +static const struct media_entity_operations xcresample_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * Power Management + */ + +static int __maybe_unused xcresample_pm_suspend(struct device *dev) +{ + struct xcresample_device *xcresample = dev_get_drvdata(dev); + + xvip_suspend(&xcresample->xvip); + + return 0; +} + +static int __maybe_unused xcresample_pm_resume(struct device *dev) +{ + struct xcresample_device *xcresample = dev_get_drvdata(dev); + + xvip_resume(&xcresample->xvip); + + return 0; +} + +/* + * Platform Device Driver + */ + +static int xcresample_parse_of(struct xcresample_device *xcresample) +{ + struct device *dev = xcresample->xvip.dev; + struct device_node *node = xcresample->xvip.dev->of_node; + struct device_node *ports; + struct device_node *port; + u32 port_id; + int ret; + + ports = of_get_child_by_name(node, "ports"); + if (ports == NULL) + ports = node; + + /* Get the format description for each pad */ + for_each_child_of_node(ports, port) { + if (port->name && (of_node_cmp(port->name, "port") == 0)) { + const struct xvip_video_format *vip_format; + + vip_format = xvip_of_get_format(port); + if (IS_ERR(vip_format)) { + dev_err(dev, "invalid format in DT"); + return PTR_ERR(vip_format); + } + + ret = of_property_read_u32(port, "reg", &port_id); + if (ret < 0) { + dev_err(dev, "no reg in DT"); + return ret; + } + + if (port_id != 0 && port_id != 1) { + dev_err(dev, "invalid reg in DT"); + return -EINVAL; + } + + xcresample->vip_formats[port_id] = vip_format; + } + } + + return 0; +} + +static int xcresample_probe(struct platform_device *pdev) +{ + struct xcresample_device *xcresample; + struct v4l2_subdev *subdev; + struct v4l2_mbus_framefmt *default_format; + int ret; + + xcresample = devm_kzalloc(&pdev->dev, sizeof(*xcresample), GFP_KERNEL); + if (!xcresample) + return -ENOMEM; + + xcresample->xvip.dev = &pdev->dev; + + ret = xcresample_parse_of(xcresample); + if (ret < 0) + return ret; + + ret = xvip_init_resources(&xcresample->xvip); + if (ret < 0) + return ret; + + /* Reset and initialize the core */ + xvip_reset(&xcresample->xvip); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xcresample->xvip.subdev; + v4l2_subdev_init(subdev, &xcresample_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xcresample_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xcresample); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Initialize default and active formats */ + default_format = &xcresample->default_formats[XVIP_PAD_SINK]; + default_format->code = xcresample->vip_formats[XVIP_PAD_SINK]->code; + default_format->field = V4L2_FIELD_NONE; + default_format->colorspace = V4L2_COLORSPACE_SRGB; + xvip_get_frame_size(&xcresample->xvip, default_format); + + xcresample->formats[XVIP_PAD_SINK] = *default_format; + + default_format = &xcresample->default_formats[XVIP_PAD_SOURCE]; + *default_format = xcresample->default_formats[XVIP_PAD_SINK]; + default_format->code = xcresample->vip_formats[XVIP_PAD_SOURCE]->code; + + xcresample->formats[XVIP_PAD_SOURCE] = *default_format; + + xcresample->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xcresample->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &xcresample_media_ops; + ret = media_entity_pads_init(&subdev->entity, 2, xcresample->pads); + if (ret < 0) + goto error; + + v4l2_ctrl_handler_init(&xcresample->ctrl_handler, 2); + xcresample_field.def = + (xvip_read(&xcresample->xvip, XCRESAMPLE_ENCODING) & + XCRESAMPLE_ENCODING_FIELD) ? 1 : 0; + v4l2_ctrl_new_custom(&xcresample->ctrl_handler, &xcresample_field, + NULL); + xcresample_chroma.def = + (xvip_read(&xcresample->xvip, XCRESAMPLE_ENCODING) & + XCRESAMPLE_ENCODING_CHROMA) ? 1 : 0; + v4l2_ctrl_new_custom(&xcresample->ctrl_handler, &xcresample_chroma, + NULL); + if (xcresample->ctrl_handler.error) { + dev_err(&pdev->dev, "failed to add controls\n"); + ret = xcresample->ctrl_handler.error; + goto error; + } + subdev->ctrl_handler = &xcresample->ctrl_handler; + + platform_set_drvdata(pdev, xcresample); + + xvip_print_version(&xcresample->xvip); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + return 0; + +error: + v4l2_ctrl_handler_free(&xcresample->ctrl_handler); + media_entity_cleanup(&subdev->entity); + xvip_cleanup_resources(&xcresample->xvip); + return ret; +} + +static int xcresample_remove(struct platform_device *pdev) +{ + struct xcresample_device *xcresample = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xcresample->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&xcresample->ctrl_handler); + media_entity_cleanup(&subdev->entity); + + xvip_cleanup_resources(&xcresample->xvip); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xcresample_pm_ops, xcresample_pm_suspend, + xcresample_pm_resume); + +static const struct of_device_id xcresample_of_id_table[] = { + { .compatible = "xlnx,v-cresample-4.0" }, + { } +}; +MODULE_DEVICE_TABLE(of, xcresample_of_id_table); + +static struct platform_driver xcresample_driver = { + .driver = { + .name = "xilinx-cresample", + .pm = &xcresample_pm_ops, + .of_match_table = xcresample_of_id_table, + }, + .probe = xcresample_probe, + .remove = xcresample_remove, +}; + +module_platform_driver(xcresample_driver); + +MODULE_DESCRIPTION("Xilinx Chroma Resampler Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c new file mode 100644 index 000000000000..fc58a5268a9e --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c @@ -0,0 +1,2023 @@ +/* + * Xilinx MIPI CSI2 Subsystem + * + * Copyright (C) 2016 Xilinx, Inc. + * + * Contacts: Vishal Sagar <vsagar@xilinx.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <dt-bindings/media/xilinx-vip.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/compiler.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/spinlock_types.h> +#include <linux/types.h> +#include <linux/v4l2-subdev.h> +#include <linux/xilinx-csi2rxss.h> +#include <linux/xilinx-v4l2-controls.h> +#include <media/media-entity.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> +#include "xilinx-vip.h" + +/* + * MIPI CSI2 Rx register map, bitmask and offsets + */ +#define XCSI_CCR_OFFSET 0x00000000 +#define XCSI_CCR_SOFTRESET_SHIFT 1 +#define XCSI_CCR_COREENB_SHIFT 0 +#define XCSI_CCR_SOFTRESET_MASK BIT(XCSI_CCR_SOFTRESET_SHIFT) +#define XCSI_CCR_COREENB_MASK BIT(XCSI_CCR_COREENB_SHIFT) + +#define XCSI_PCR_OFFSET 0x00000004 +#define XCSI_PCR_MAXLANES_MASK 0x00000018 +#define XCSI_PCR_ACTLANES_MASK 0x00000003 +#define XCSI_PCR_MAXLANES_SHIFT 3 +#define XCSI_PCR_ACTLANES_SHIFT 0 + +#define XCSI_CSR_OFFSET 0x00000010 +#define XCSI_CSR_PKTCOUNT_SHIFT 16 +#define XCSI_CSR_SPFIFOFULL_SHIFT 3 +#define XCSI_CSR_SPFIFONE_SHIFT 2 +#define XCSI_CSR_SLBF_SHIFT 1 +#define XCSI_CSR_RIPCD_SHIFT 0 +#define XCSI_CSR_PKTCOUNT_MASK 0xFFFF0000 +#define XCSI_CSR_SPFIFOFULL_MASK BIT(XCSI_CSR_SPFIFOFULL_SHIFT) +#define XCSI_CSR_SPFIFONE_MASK BIT(XCSI_CSR_SPFIFONE_SHIFT) +#define XCSI_CSR_SLBF_MASK BIT(XCSI_CSR_SLBF_SHIFT) +#define XCSI_CSR_RIPCD_MASK BIT(XCSI_CSR_RIPCD_SHIFT) + +#define XCSI_GIER_OFFSET 0x00000020 +#define XCSI_GIER_GIE_SHIFT 0 +#define XCSI_GIER_GIE_MASK BIT(XCSI_GIER_GIE_SHIFT) +#define XCSI_GIER_SET 1 +#define XCSI_GIER_RESET 0 + +#define XCSI_ISR_OFFSET 0x00000024 +#define XCSI_ISR_FR_SHIFT 31 +#define XCSI_ISR_VCX_SHIFT 30 +#define XCSI_ISR_ILC_SHIFT 21 +#define XCSI_ISR_SPFIFOF_SHIFT 20 +#define XCSI_ISR_SPFIFONE_SHIFT 19 +#define XCSI_ISR_SLBF_SHIFT 18 +#define XCSI_ISR_STOP_SHIFT 17 +#define XCSI_ISR_SOTERR_SHIFT 13 +#define XCSI_ISR_SOTSYNCERR_SHIFT 12 +#define XCSI_ISR_ECC2BERR_SHIFT 11 +#define XCSI_ISR_ECC1BERR_SHIFT 10 +#define XCSI_ISR_CRCERR_SHIFT 9 +#define XCSI_ISR_DATAIDERR_SHIFT 8 +#define XCSI_ISR_VC3FSYNCERR_SHIFT 7 +#define XCSI_ISR_VC3FLVLERR_SHIFT 6 +#define XCSI_ISR_VC2FSYNCERR_SHIFT 5 +#define XCSI_ISR_VC2FLVLERR_SHIFT 4 +#define XCSI_ISR_VC1FSYNCERR_SHIFT 3 +#define XCSI_ISR_VC1FLVLERR_SHIFT 2 +#define XCSI_ISR_VC0FSYNCERR_SHIFT 1 +#define XCSI_ISR_VC0FLVLERR_SHIFT 0 +#define XCSI_ISR_FR_MASK BIT(XCSI_ISR_FR_SHIFT) +#define XCSI_ISR_VCX_MASK BIT(XCSI_ISR_VCX_SHIFT) +#define XCSI_ISR_ILC_MASK BIT(XCSI_ISR_ILC_SHIFT) +#define XCSI_ISR_SPFIFOF_MASK BIT(XCSI_ISR_SPFIFOF_SHIFT) +#define XCSI_ISR_SPFIFONE_MASK BIT(XCSI_ISR_SPFIFONE_SHIFT) +#define XCSI_ISR_SLBF_MASK BIT(XCSI_ISR_SLBF_SHIFT) +#define XCSI_ISR_STOP_MASK BIT(XCSI_ISR_STOP_SHIFT) +#define XCSI_ISR_SOTERR_MASK BIT(XCSI_ISR_SOTERR_SHIFT) +#define XCSI_ISR_SOTSYNCERR_MASK BIT(XCSI_ISR_SOTSYNCERR_SHIFT) +#define XCSI_ISR_ECC2BERR_MASK BIT(XCSI_ISR_ECC2BERR_SHIFT) +#define XCSI_ISR_ECC1BERR_MASK BIT(XCSI_ISR_ECC1BERR_SHIFT) +#define XCSI_ISR_CRCERR_MASK BIT(XCSI_ISR_CRCERR_SHIFT) +#define XCSI_ISR_DATAIDERR_MASK BIT(XCSI_ISR_DATAIDERR_SHIFT) +#define XCSI_ISR_VC3FSYNCERR_MASK BIT(XCSI_ISR_VC3FSYNCERR_SHIFT) +#define XCSI_ISR_VC3FLVLERR_MASK BIT(XCSI_ISR_VC3FLVLERR_SHIFT) +#define XCSI_ISR_VC2FSYNCERR_MASK BIT(XCSI_ISR_VC2FSYNCERR_SHIFT) +#define XCSI_ISR_VC2FLVLERR_MASK BIT(XCSI_ISR_VC2FLVLERR_SHIFT) +#define XCSI_ISR_VC1FSYNCERR_MASK BIT(XCSI_ISR_VC1FSYNCERR_SHIFT) +#define XCSI_ISR_VC1FLVLERR_MASK BIT(XCSI_ISR_VC1FLVLERR_SHIFT) +#define XCSI_ISR_VC0FSYNCERR_MASK BIT(XCSI_ISR_VC0FSYNCERR_SHIFT) +#define XCSI_ISR_VC0FLVLERR_MASK BIT(XCSI_ISR_VC0FLVLERR_SHIFT) +#define XCSI_ISR_ALLINTR_MASK 0xC03FFFFF + +#define XCSI_INTR_PROT_MASK (XCSI_ISR_VC3FSYNCERR_MASK | \ + XCSI_ISR_VC3FLVLERR_MASK | \ + XCSI_ISR_VC2FSYNCERR_MASK | \ + XCSI_ISR_VC2FLVLERR_MASK | \ + XCSI_ISR_VC1FSYNCERR_MASK | \ + XCSI_ISR_VC1FLVLERR_MASK | \ + XCSI_ISR_VC0FSYNCERR_MASK | \ + XCSI_ISR_VC0FLVLERR_MASK | \ + XCSI_ISR_VCX_MASK) + +#define XCSI_INTR_PKTLVL_MASK (XCSI_ISR_ECC2BERR_MASK | \ + XCSI_ISR_ECC1BERR_MASK | \ + XCSI_ISR_CRCERR_MASK | \ + XCSI_ISR_DATAIDERR_MASK) + +#define XCSI_INTR_DPHY_MASK (XCSI_ISR_SOTERR_MASK | \ + XCSI_ISR_SOTSYNCERR_MASK) + +#define XCSI_INTR_SPKT_MASK (XCSI_ISR_SPFIFOF_MASK | \ + XCSI_ISR_SPFIFONE_MASK) + +#define XCSI_INTR_FRAMERCVD_MASK (XCSI_ISR_FR_MASK) + +#define XCSI_INTR_ERR_MASK (XCSI_ISR_ILC_MASK | \ + XCSI_ISR_SLBF_MASK | \ + XCSI_ISR_STOP_MASK) + +#define XCSI_IER_OFFSET 0x00000028 +#define XCSI_IER_FR_SHIFT 31 +#define XCSI_IER_VCX_SHIFT 30 +#define XCSI_IER_ILC_SHIFT 21 +#define XCSI_IER_SPFIFOF_SHIFT 20 +#define XCSI_IER_SPFIFONE_SHIFT 19 +#define XCSI_IER_SLBF_SHIFT 18 +#define XCSI_IER_STOP_SHIFT 17 +#define XCSI_IER_SOTERR_SHIFT 13 +#define XCSI_IER_SOTSYNCERR_SHIFT 12 +#define XCSI_IER_ECC2BERR_SHIFT 11 +#define XCSI_IER_ECC1BERR_SHIFT 10 +#define XCSI_IER_CRCERR_SHIFT 9 +#define XCSI_IER_DATAIDERR_SHIFT 8 +#define XCSI_IER_VC3FSYNCERR_SHIFT 7 +#define XCSI_IER_VC3FLVLERR_SHIFT 6 +#define XCSI_IER_VC2FSYNCERR_SHIFT 5 +#define XCSI_IER_VC2FLVLERR_SHIFT 4 +#define XCSI_IER_VC1FSYNCERR_SHIFT 3 +#define XCSI_IER_VC1FLVLERR_SHIFT 2 +#define XCSI_IER_VC0FSYNCERR_SHIFT 1 +#define XCSI_IER_VC0FLVLERR_SHIFT 0 +#define XCSI_IER_FR_MASK BIT(XCSI_IER_FR_SHIFT) +#define XCSI_IER_VCX_MASK BIT(XCSI_IER_VCX_SHIFT) +#define XCSI_IER_ILC_MASK BIT(XCSI_IER_ILC_SHIFT) +#define XCSI_IER_SPFIFOF_MASK BIT(XCSI_IER_SPFIFOF_SHIFT) +#define XCSI_IER_SPFIFONE_MASK BIT(XCSI_IER_SPFIFONE_SHIFT) +#define XCSI_IER_SLBF_MASK BIT(XCSI_IER_SLBF_SHIFT) +#define XCSI_IER_STOP_MASK BIT(XCSI_IER_STOP_SHIFT) +#define XCSI_IER_SOTERR_MASK BIT(XCSI_IER_SOTERR_SHIFT) +#define XCSI_IER_SOTSYNCERR_MASK BIT(XCSI_IER_SOTSYNCERR_SHIFT) +#define XCSI_IER_ECC2BERR_MASK BIT(XCSI_IER_ECC2BERR_SHIFT) +#define XCSI_IER_ECC1BERR_MASK BIT(XCSI_IER_ECC1BERR_SHIFT) +#define XCSI_IER_CRCERR_MASK BIT(XCSI_IER_CRCERR_SHIFT) +#define XCSI_IER_DATAIDERR_MASK BIT(XCSI_IER_DATAIDERR_SHIFT) +#define XCSI_IER_VC3FSYNCERR_MASK BIT(XCSI_IER_VC3FSYNCERR_SHIFT) +#define XCSI_IER_VC3FLVLERR_MASK BIT(XCSI_IER_VC3FLVLERR_SHIFT) +#define XCSI_IER_VC2FSYNCERR_MASK BIT(XCSI_IER_VC2FSYNCERR_SHIFT) +#define XCSI_IER_VC2FLVLERR_MASK BIT(XCSI_IER_VC2FLVLERR_SHIFT) +#define XCSI_IER_VC1FSYNCERR_MASK BIT(XCSI_IER_VC1FSYNCERR_SHIFT) +#define XCSI_IER_VC1FLVLERR_MASK BIT(XCSI_IER_VC1FLVLERR_SHIFT) +#define XCSI_IER_VC0FSYNCERR_MASK BIT(XCSI_IER_VC0FSYNCERR_SHIFT) +#define XCSI_IER_VC0FLVLERR_MASK BIT(XCSI_IER_VC0FLVLERR_SHIFT) +#define XCSI_IER_ALLINTR_MASK 0xC03FFFFF + +#define XCSI_SPKTR_OFFSET 0x00000030 +#define XCSI_SPKTR_DATA_SHIFT 8 +#define XCSI_SPKTR_VC_SHIFT 6 +#define XCSI_SPKTR_DT_SHIFT 0 +#define XCSI_SPKTR_DATA_MASK 0x00FFFF00 +#define XCSI_SPKTR_VC_MASK 0x000000C0 +#define XCSI_SPKTR_DT_MASK 0x0000003F + +#define XCSI_VCXR_OFFSET 0x00000034 +#define XCSI_VCXR_VC15FSYNCERR_MASK BIT(23) +#define XCSI_VCXR_VC15FLVLERR_MASK BIT(22) +#define XCSI_VCXR_VC14FSYNCERR_MASK BIT(21) +#define XCSI_VCXR_VC14FLVLERR_MASK BIT(20) +#define XCSI_VCXR_VC13FSYNCERR_MASK BIT(19) +#define XCSI_VCXR_VC13FLVLERR_MASK BIT(18) +#define XCSI_VCXR_VC12FSYNCERR_MASK BIT(17) +#define XCSI_VCXR_VC12FLVLERR_MASK BIT(16) +#define XCSI_VCXR_VC11FSYNCERR_MASK BIT(15) +#define XCSI_VCXR_VC11FLVLERR_MASK BIT(14) +#define XCSI_VCXR_VC10FSYNCERR_MASK BIT(13) +#define XCSI_VCXR_VC10FLVLERR_MASK BIT(12) +#define XCSI_VCXR_VC9FSYNCERR_MASK BIT(11) +#define XCSI_VCXR_VC9FLVLERR_MASK BIT(10) +#define XCSI_VCXR_VC8FSYNCERR_MASK BIT(9) +#define XCSI_VCXR_VC8FLVLERR_MASK BIT(8) +#define XCSI_VCXR_VC7FSYNCERR_MASK BIT(7) +#define XCSI_VCXR_VC7FLVLERR_MASK BIT(6) +#define XCSI_VCXR_VC6FSYNCERR_MASK BIT(5) +#define XCSI_VCXR_VC6FLVLERR_MASK BIT(4) +#define XCSI_VCXR_VC5FSYNCERR_MASK BIT(3) +#define XCSI_VCXR_VC5FLVLERR_MASK BIT(2) +#define XCSI_VCXR_VC4FSYNCERR_MASK BIT(1) +#define XCSI_VCXR_VC4FLVLERR_MASK BIT(0) +#define XCSI_VCXR_MASK 0x00FFFFFF + +#define XCSI_CLKINFR_OFFSET 0x0000003C +#define XCSI_CLKINFR_STOP_SHIFT 1 +#define XCSI_CLKINFR_STOP_MASK BIT(XCSI_CLKINFR_STOP_SHIFT) + +#define XCSI_L0INFR_OFFSET 0x00000040 +#define XCSI_L1INFR_OFFSET 0x00000044 +#define XCSI_L2INFR_OFFSET 0x00000048 +#define XCSI_L3INFR_OFFSET 0x0000004C +#define XCSI_LXINFR_STOP_SHIFT 5 +#define XCSI_LXINFR_SOTERR_SHIFT 1 +#define XCSI_LXINFR_SOTSYNCERR_SHIFT 0 +#define XCSI_LXINFR_STOP_MASK BIT(XCSI_LXINFR_STOP_SHIFT) +#define XCSI_LXINFR_SOTERR_MASK BIT(XCSI_LXINFR_SOTERR_SHIFT) +#define XCSI_LXINFR_SOTSYNCERR_MASK BIT(XCSI_LXINFR_SOTSYNCERR_SHIFT) + +#define XCSI_VC0INF1R_OFFSET 0x00000060 +#define XCSI_VC1INF1R_OFFSET 0x00000068 +#define XCSI_VC2INF1R_OFFSET 0x00000070 +#define XCSI_VC3INF1R_OFFSET 0x00000078 +#define XCSI_VC4INF1R_OFFSET 0x00000080 +#define XCSI_VC5INF1R_OFFSET 0x00000088 +#define XCSI_VC6INF1R_OFFSET 0x00000090 +#define XCSI_VC7INF1R_OFFSET 0x00000098 +#define XCSI_VC8INF1R_OFFSET 0x000000A0 +#define XCSI_VC9INF1R_OFFSET 0x000000A8 +#define XCSI_VC10INF1R_OFFSET 0x000000B0 +#define XCSI_VC11INF1R_OFFSET 0x000000B8 +#define XCSI_VC12INF1R_OFFSET 0x000000C0 +#define XCSI_VC13INF1R_OFFSET 0x000000C8 +#define XCSI_VC14INF1R_OFFSET 0x000000D0 +#define XCSI_VC15INF1R_OFFSET 0x000000D8 +#define XCSI_VCXINF1R_LINECOUNT_SHIFT 16 +#define XCSI_VCXINF1R_BYTECOUNT_SHIFT 0 +#define XCSI_VCXINF1R_LINECOUNT_MASK 0xFFFF0000 +#define XCSI_VCXINF1R_BYTECOUNT_MASK 0x0000FFFF + +#define XCSI_VC0INF2R_OFFSET 0x00000064 +#define XCSI_VC1INF2R_OFFSET 0x0000006C +#define XCSI_VC2INF2R_OFFSET 0x00000074 +#define XCSI_VC3INF2R_OFFSET 0x0000007C +#define XCSI_VC4INF2R_OFFSET 0x00000084 +#define XCSI_VC5INF2R_OFFSET 0x0000008C +#define XCSI_VC6INF2R_OFFSET 0x00000094 +#define XCSI_VC7INF2R_OFFSET 0x0000009C +#define XCSI_VC8INF2R_OFFSET 0x000000A4 +#define XCSI_VC9INF2R_OFFSET 0x000000AC +#define XCSI_VC10INF2R_OFFSET 0x000000B4 +#define XCSI_VC11INF2R_OFFSET 0x000000BC +#define XCSI_VC12INF2R_OFFSET 0x000000C4 +#define XCSI_VC13INF2R_OFFSET 0x000000CC +#define XCSI_VC14INF2R_OFFSET 0x000000D4 +#define XCSI_VC15INF2R_OFFSET 0x000000DC +#define XCSI_VCXINF2R_DATATYPE_SHIFT 0 +#define XCSI_VCXINF2R_DATATYPE_MASK 0x0000003F + +#define XDPHY_CTRLREG_OFFSET 0x0 +#define XDPHY_CTRLREG_DPHYEN_SHIFT 1 +#define XDPHY_CTRLREG_DPHYEN_MASK BIT(XDPHY_CTRLREG_DPHYEN_SHIFT) + +#define XDPHY_CLKSTATREG_OFFSET 0x18 +#define XDPHY_CLKSTATREG_MODE_SHIFT 0 +#define XDPHY_CLKSTATREG_MODE_MASK 0x3 +#define XDPHY_LOW_POWER_MODE 0x0 +#define XDPHY_HI_SPEED_MODE 0x1 +#define XDPHY_ESC_MODE 0x2 + +/* + * Interrupt mask + */ +#define XCSI_INTR_MASK (XCSI_ISR_ALLINTR_MASK & ~XCSI_ISR_STOP_MASK) +/* + * Timeout for reset + */ +#define XCSI_TIMEOUT_VAL (1000) /* us */ + +/* + * Max string length for CSI Data type string + */ +#define MAX_XIL_CSIDT_STR_LENGTH 64 + +/* + * Maximum number of short packet events per file handle. + */ +#define XCSI_MAX_SPKT (512) + +/* Number of media pads */ +#define XILINX_CSI_MEDIA_PADS (2) + +#define XCSI_DEFAULT_WIDTH (1920) +#define XCSI_DEFAULT_HEIGHT (1080) + +#define XCSI_DPHY_CLK_MIN 197000000000UL +#define XCSI_DPHY_CLK_MAX 203000000000UL +#define XCSI_DPHY_CLK_REQ 200000000000UL + +/* + * Macro to return "true" or "false" string if bit is set + */ +#define XCSI_GET_BITSET_STR(val, mask) (val) & (mask) ? "true" : "false" + +#define XCSI_CLK_PROP BIT(0) + +/** + * struct xcsi2rxss_feature - dt or IP property structure + * @flags: Bitmask of properties enabled in IP or dt + */ +struct xcsi2rxss_feature { + u32 flags; +}; + +enum CSI_DataTypes { + MIPI_CSI_DT_FRAME_START_CODE = 0x00, + MIPI_CSI_DT_FRAME_END_CODE, + MIPI_CSI_DT_LINE_START_CODE, + MIPI_CSI_DT_LINE_END_CODE, + MIPI_CSI_DT_SYNC_RSVD_04, + MIPI_CSI_DT_SYNC_RSVD_05, + MIPI_CSI_DT_SYNC_RSVD_06, + MIPI_CSI_DT_SYNC_RSVD_07, + MIPI_CSI_DT_GSPKT_08, + MIPI_CSI_DT_GSPKT_09, + MIPI_CSI_DT_GSPKT_0A, + MIPI_CSI_DT_GSPKT_0B, + MIPI_CSI_DT_GSPKT_0C, + MIPI_CSI_DT_GSPKT_0D, + MIPI_CSI_DT_GSPKT_0E, + MIPI_CSI_DT_GSPKT_0F, + MIPI_CSI_DT_GLPKT_10, + MIPI_CSI_DT_GLPKT_11, + MIPI_CSI_DT_GLPKT_12, + MIPI_CSI_DT_GLPKT_13, + MIPI_CSI_DT_GLPKT_14, + MIPI_CSI_DT_GLPKT_15, + MIPI_CSI_DT_GLPKT_16, + MIPI_CSI_DT_GLPKT_17, + MIPI_CSI_DT_YUV_420_8B, + MIPI_CSI_DT_YUV_420_10B, + MIPI_CSI_DT_YUV_420_8B_LEGACY, + MIPI_CSI_DT_YUV_RSVD, + MIPI_CSI_DT_YUV_420_8B_CSPS, + MIPI_CSI_DT_YUV_420_10B_CSPS, + MIPI_CSI_DT_YUV_422_8B, + MIPI_CSI_DT_YUV_422_10B, + MIPI_CSI_DT_RGB_444, + MIPI_CSI_DT_RGB_555, + MIPI_CSI_DT_RGB_565, + MIPI_CSI_DT_RGB_666, + MIPI_CSI_DT_RGB_888, + MIPI_CSI_DT_RGB_RSVD_25, + MIPI_CSI_DT_RGB_RSVD_26, + MIPI_CSI_DT_RGB_RSVD_27, + MIPI_CSI_DT_RAW_6, + MIPI_CSI_DT_RAW_7, + MIPI_CSI_DT_RAW_8, + MIPI_CSI_DT_RAW_10, + MIPI_CSI_DT_RAW_12, + MIPI_CSI_DT_RAW_14, + MIPI_CSI_DT_RAW_16, + MIPI_CSI_DT_RAW_20, + MIPI_CSI_DT_USER_30, + MIPI_CSI_DT_USER_31, + MIPI_CSI_DT_USER_32, + MIPI_CSI_DT_USER_33, + MIPI_CSI_DT_USER_34, + MIPI_CSI_DT_USER_35, + MIPI_CSI_DT_USER_36, + MIPI_CSI_DT_USER_37, + MIPI_CSI_DT_RSVD_38, + MIPI_CSI_DT_RSVD_39, + MIPI_CSI_DT_RSVD_3A, + MIPI_CSI_DT_RSVD_3B, + MIPI_CSI_DT_RSVD_3C, + MIPI_CSI_DT_RSVD_3D, + MIPI_CSI_DT_RSVD_3E, + MIPI_CSI_DT_RSVD_3F +}; + +/** + * struct pixel_format - Data Type to string name structure + * @PixelFormat: MIPI CSI2 Data type + * @PixelFormatStr: String name of Data Type + */ +struct pixel_format { + enum CSI_DataTypes PixelFormat; + char PixelFormatStr[MAX_XIL_CSIDT_STR_LENGTH]; +}; + +/** + * struct xcsi2rxss_event - Event log structure + * @mask: Event mask + * @name: Name of the event + * @counter: Count number of events + */ +struct xcsi2rxss_event { + u32 mask; + const char * const name; + unsigned int counter; +}; + +/* + * struct xcsi2rxss_core - Core configuration CSI2 Rx Subsystem device structure + * @dev: Platform structure + * @iomem: Base address of subsystem + * @irq: requested irq number + * @dphy_present: Flag for DPHY register interface presence + * @dphy_offset: DPHY registers offset + * @enable_active_lanes: If number of active lanes can be modified + * @max_num_lanes: Maximum number of lanes present + * @vfb: Video Format Bridge enabled or not + * @ppc: pixels per clock + * @vc: Virtual Channel + * @axis_tdata_width: AXI Stream data width + * @datatype: Data type filter + * @pxlformat: String with CSI pixel format from IP + * @num_lanes: Number of lanes requested from application + * @events: Structure to maintain event logs + * @vcx_events: Structure to maintain VCX event logs + * @en_vcx: If more than 4 VC are enabled. + * @cfg: Pointer to csi2rxss config structure + * @lite_aclk: AXI4-Lite interface clock + * @video_aclk: Video clock + * @dphy_clk_200M: 200MHz DPHY clock + */ +struct xcsi2rxss_core { + struct device *dev; + void __iomem *iomem; + int irq; + u32 dphy_offset; + bool dphy_present; + bool enable_active_lanes; + u32 max_num_lanes; + bool vfb; + u32 ppc; + u32 vc; + u32 axis_tdata_width; + u32 datatype; + const char *pxlformat; + u32 num_lanes; + struct xcsi2rxss_event *events; + struct xcsi2rxss_event *vcx_events; + bool en_vcx; + const struct xcsi2rxss_feature *cfg; + struct clk *lite_aclk; + struct clk *video_aclk; + struct clk *dphy_clk_200M; +}; + +/** + * struct xcsi2rxss_state - CSI2 Rx Subsystem device structure + * @core: Core structure for MIPI CSI2 Rx Subsystem + * @subdev: The v4l2 subdev structure + * @ctrl_handler: control handler + * @formats: Active V4L2 formats on each pad + * @default_format: default V4L2 media bus format + * @vip_format: format information corresponding to the active format + * @event: Holds the short packet event + * @lock: mutex for serializing operations + * @pads: media pads + * @npads: number of pads + * @streaming: Flag for storing streaming state + * @suspended: Flag for storing suspended state + * + * This structure contains the device driver related parameters + */ +struct xcsi2rxss_state { + struct xcsi2rxss_core core; + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_format; + const struct xvip_video_format *vip_format; + struct v4l2_event event; + struct mutex lock; + struct media_pad pads[XILINX_CSI_MEDIA_PADS]; + unsigned int npads; + bool streaming; + bool suspended; +}; + +static const struct xcsi2rxss_feature xlnx_csi2rxss_v4_0 = { + .flags = XCSI_CLK_PROP, +}; + +static const struct xcsi2rxss_feature xlnx_csi2rxss_v2_0 = { + .flags = 0, +}; + +static const struct of_device_id xcsi2rxss_of_id_table[] = { + { .compatible = "xlnx,mipi-csi2-rx-subsystem-2.0", + .data = &xlnx_csi2rxss_v2_0 }, + { .compatible = "xlnx,mipi-csi2-rx-subsystem-3.0", + .data = &xlnx_csi2rxss_v2_0 }, + { .compatible = "xlnx,mipi-csi2-rx-subsystem-4.0", + .data = &xlnx_csi2rxss_v4_0 }, + { } +}; +MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table); + +static inline struct xcsi2rxss_state * +to_xcsi2rxssstate(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xcsi2rxss_state, subdev); +} + +/* + * Regsiter related operations + */ +static inline u32 xcsi2rxss_read(struct xcsi2rxss_core *xcsi2rxss, + u32 addr) +{ + return ioread32(xcsi2rxss->iomem + addr); +} + +static inline void xcsi2rxss_write(struct xcsi2rxss_core *xcsi2rxss, + u32 addr, + u32 value) +{ + iowrite32(value, xcsi2rxss->iomem + addr); +} + +static inline void xcsi2rxss_clr(struct xcsi2rxss_core *xcsi2rxss, + u32 addr, + u32 clr) +{ + xcsi2rxss_write(xcsi2rxss, + addr, + xcsi2rxss_read(xcsi2rxss, addr) & ~clr); +} + +static inline void xcsi2rxss_set(struct xcsi2rxss_core *xcsi2rxss, + u32 addr, + u32 set) +{ + xcsi2rxss_write(xcsi2rxss, + addr, + xcsi2rxss_read(xcsi2rxss, addr) | set); +} + +static const struct pixel_format pixel_formats[] = { + { MIPI_CSI_DT_YUV_420_8B, "YUV420_8bit" }, + { MIPI_CSI_DT_YUV_420_10B, "YUV420_10bit" }, + { MIPI_CSI_DT_YUV_420_8B_LEGACY, "Legacy_YUV420_8bit" }, + { MIPI_CSI_DT_YUV_420_8B_CSPS, "YUV420_8bit_CSPS" }, + { MIPI_CSI_DT_YUV_420_10B_CSPS, "YUV420_10bit_CSPS" }, + { MIPI_CSI_DT_YUV_422_8B, "YUV422_8bit" }, + { MIPI_CSI_DT_YUV_422_10B, "YUV422_10bit" }, + { MIPI_CSI_DT_RGB_444, "RGB444" }, + { MIPI_CSI_DT_RGB_555, "RGB555" }, + { MIPI_CSI_DT_RGB_565, "RGB565" }, + { MIPI_CSI_DT_RGB_666, "RGB666" }, + { MIPI_CSI_DT_RGB_888, "RGB888" }, + { MIPI_CSI_DT_RAW_6, "RAW6" }, + { MIPI_CSI_DT_RAW_7, "RAW7" }, + { MIPI_CSI_DT_RAW_8, "RAW8" }, + { MIPI_CSI_DT_RAW_10, "RAW10" }, + { MIPI_CSI_DT_RAW_12, "RAW12" }, + { MIPI_CSI_DT_RAW_14, "RAW14"}, + { MIPI_CSI_DT_RAW_16, "RAW16"}, + { MIPI_CSI_DT_RAW_20, "RAW20"} +}; + +static struct xcsi2rxss_event xcsi2rxss_events[] = { + { XCSI_ISR_FR_MASK, "Frame Received", 0 }, + { XCSI_ISR_VCX_MASK, "VCX Frame Errors", 0 }, + { XCSI_ISR_ILC_MASK, "Invalid Lane Count Error", 0 }, + { XCSI_ISR_SPFIFOF_MASK, "Short Packet FIFO OverFlow Error", 0 }, + { XCSI_ISR_SPFIFONE_MASK, "Short Packet FIFO Not Empty", 0 }, + { XCSI_ISR_SLBF_MASK, "Streamline Buffer Full Error", 0 }, + { XCSI_ISR_STOP_MASK, "Lane Stop State", 0 }, + { XCSI_ISR_SOTERR_MASK, "SOT Error", 0 }, + { XCSI_ISR_SOTSYNCERR_MASK, "SOT Sync Error", 0 }, + { XCSI_ISR_ECC2BERR_MASK, "2 Bit ECC Unrecoverable Error", 0 }, + { XCSI_ISR_ECC1BERR_MASK, "1 Bit ECC Recoverable Error", 0 }, + { XCSI_ISR_CRCERR_MASK, "CRC Error", 0 }, + { XCSI_ISR_DATAIDERR_MASK, "Data Id Error", 0 }, + { XCSI_ISR_VC3FSYNCERR_MASK, "Virtual Channel 3 Frame Sync Error", 0 }, + { XCSI_ISR_VC3FLVLERR_MASK, "Virtual Channel 3 Frame Level Error", 0 }, + { XCSI_ISR_VC2FSYNCERR_MASK, "Virtual Channel 2 Frame Sync Error", 0 }, + { XCSI_ISR_VC2FLVLERR_MASK, "Virtual Channel 2 Frame Level Error", 0 }, + { XCSI_ISR_VC1FSYNCERR_MASK, "Virtual Channel 1 Frame Sync Error", 0 }, + { XCSI_ISR_VC1FLVLERR_MASK, "Virtual Channel 1 Frame Level Error", 0 }, + { XCSI_ISR_VC0FSYNCERR_MASK, "Virtual Channel 0 Frame Sync Error", 0 }, + { XCSI_ISR_VC0FLVLERR_MASK, "Virtual Channel 0 Frame Level Error", 0 } +}; + +#define XMIPICSISS_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events) + +#define XMIPICSISS_VCX_START (4) +#define XMIPICSISS_MAX_VC (4) +#define XMIPICSISS_MAX_VCX (16) + +/* There are 2 events frame sync and frame level error per VC */ +#define XMIPICSISS_VCX_NUM_EVENTS ((XMIPICSISS_MAX_VCX -\ + XMIPICSISS_MAX_VC) * 2) + +/** + * xcsi2rxss_clr_and_set - Clear and set the register with a bitmask + * @xcsi2rxss: Xilinx MIPI CSI2 Rx Subsystem subdev core struct + * @addr: address of register + * @clr: bitmask to be cleared + * @set: bitmask to be set + * + * Clear a bit(s) of mask @clr in the register at address @addr, then set + * a bit(s) of mask @set in the register after. + */ +static void xcsi2rxss_clr_and_set(struct xcsi2rxss_core *xcsi2rxss, + u32 addr, u32 clr, u32 set) +{ + u32 reg; + + reg = xcsi2rxss_read(xcsi2rxss, addr); + reg &= ~clr; + reg |= set; + xcsi2rxss_write(xcsi2rxss, addr, reg); +} + +/** + * xcsi2rxss_pxlfmtstrtodt - Convert pixel format string got from dts + * to data type. + * @pxlfmtstr: String obtained while parsing device node + * + * This function takes a CSI pixel format string obtained while parsing + * device tree node and converts it to data type. + * + * Eg. "RAW8" string is converted to 0x2A. + * Refer to MIPI CSI2 specification for details. + * + * Return: Equivalent pixel format value from table + */ +static u32 xcsi2rxss_pxlfmtstrtodt(const char *pxlfmtstr) +{ + u32 Index; + u32 MaxEntries = ARRAY_SIZE(pixel_formats); + + for (Index = 0; Index < MaxEntries; Index++) { + if (!strncmp(pixel_formats[Index].PixelFormatStr, + pxlfmtstr, MAX_XIL_CSIDT_STR_LENGTH)) + return pixel_formats[Index].PixelFormat; + } + + return -EINVAL; +} + +/** + * xcsi2rxss_pxlfmtdttostr - Convert pixel format data type to string. + * @datatype: MIPI CSI-2 Data Type + * + * This function takes a CSI pixel format data type and returns a + * pointer to the string name. + * + * Eg. 0x2A returns "RAW8" string. + * Refer to MIPI CSI2 specification for details. + * + * Return: Equivalent pixel format string from table + */ +static const char *xcsi2rxss_pxlfmtdttostr(u32 datatype) +{ + u32 Index; + u32 MaxEntries = ARRAY_SIZE(pixel_formats); + + for (Index = 0; Index < MaxEntries; Index++) { + if (pixel_formats[Index].PixelFormat == datatype) + return pixel_formats[Index].PixelFormatStr; + } + + return NULL; +} + +/** + * xcsi2rxss_enable - Enable or disable the CSI Core + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer + * @flag: true for enabling, false for disabling + * + * This function enables/disables the MIPI CSI2 Rx Subsystem core. + * After enabling the CSI2 Rx core, the DPHY is enabled in case the + * register interface for it is present. + */ +static void xcsi2rxss_enable(struct xcsi2rxss_core *core, bool flag) +{ + u32 DphyCtrlRegOffset = core->dphy_offset + XDPHY_CTRLREG_OFFSET; + + if (flag) { + xcsi2rxss_write(core, XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK); + if (core->dphy_present) + xcsi2rxss_write(core, DphyCtrlRegOffset, + XDPHY_CTRLREG_DPHYEN_MASK); + } else { + xcsi2rxss_write(core, XCSI_CCR_OFFSET, 0); + if (core->dphy_present) + xcsi2rxss_write(core, DphyCtrlRegOffset, 0); + } + +} + +/** + * xcsi2rxss_interrupts_enable - Enable or disable CSI interrupts + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer + * @flag: true for enabling, false for disabling + * + * This function enables/disables the interrupts for the MIPI CSI2 + * Rx Subsystem. + */ +static void xcsi2rxss_interrupts_enable(struct xcsi2rxss_core *core, bool flag) +{ + if (flag) { + xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK); + xcsi2rxss_write(core, XCSI_IER_OFFSET, XCSI_INTR_MASK); + xcsi2rxss_set(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK); + } else { + xcsi2rxss_clr(core, XCSI_IER_OFFSET, XCSI_INTR_MASK); + xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK); + } +} + +/** + * xcsi2rxss_reset - Does a soft reset of the MIPI CSI2 Rx Subsystem + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer + * + * Return: 0 - on success OR -ETIME if reset times out + */ +static int xcsi2rxss_reset(struct xcsi2rxss_core *core) +{ + u32 Timeout = XCSI_TIMEOUT_VAL; + + xcsi2rxss_set(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK); + + while (xcsi2rxss_read(core, XCSI_CSR_OFFSET) & XCSI_CSR_RIPCD_MASK) { + if (Timeout == 0) { + dev_err(core->dev, "Xilinx CSI2 Rx Subsystem Soft Reset Timeout!\n"); + return -ETIME; + } + + Timeout--; + udelay(1); + } + + xcsi2rxss_clr(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK); + return 0; +} + +/** + * xcsi2rxss_irq_handler - Interrupt handler for CSI-2 + * @irq: IRQ number + * @dev_id: Pointer to device state + * + * In the interrupt handler, a list of event counters are updated for + * corresponding interrupts. This is useful to get status / debug. + * If the short packet FIFO not empty or overflow interrupt is received + * capture the short packet and notify of event occurrence + * + * Return: IRQ_HANDLED after handling interrupts + */ +static irqreturn_t xcsi2rxss_irq_handler(int irq, void *dev_id) +{ + struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)dev_id; + struct xcsi2rxss_core *core = &state->core; + u32 status; + + status = xcsi2rxss_read(core, XCSI_ISR_OFFSET) & XCSI_INTR_MASK; + dev_dbg(core->dev, "interrupt status = 0x%08x\n", status); + + if (!status) + return IRQ_NONE; + + if (status & XCSI_ISR_SPFIFONE_MASK) { + + memset(&state->event, 0, sizeof(state->event)); + + state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT; + + *((u32 *)(&state->event.u.data)) = + xcsi2rxss_read(core, XCSI_SPKTR_OFFSET); + + v4l2_subdev_notify_event(&state->subdev, &state->event); + } + + if (status & XCSI_ISR_SPFIFOF_MASK) { + dev_alert(core->dev, "Short packet FIFO overflowed\n"); + + memset(&state->event, 0, sizeof(state->event)); + + state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT_OVF; + + v4l2_subdev_notify_event(&state->subdev, &state->event); + } + + if (status & XCSI_ISR_SLBF_MASK) { + dev_alert(core->dev, "Stream Line Buffer Full!\n"); + + memset(&state->event, 0, sizeof(state->event)); + + state->event.type = V4L2_EVENT_XLNXCSIRX_SLBF; + + v4l2_subdev_notify_event(&state->subdev, &state->event); + } + + if (status & XCSI_ISR_ALLINTR_MASK) { + unsigned int i; + + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) { + if (!(status & core->events[i].mask)) + continue; + core->events[i].counter++; + dev_dbg(core->dev, "%s: %d\n", core->events[i].name, + core->events[i].counter); + } + + if (status & XCSI_ISR_VCX_MASK && core->en_vcx) { + u32 vcxstatus; + + vcxstatus = xcsi2rxss_read(core, XCSI_VCXR_OFFSET); + vcxstatus &= XCSI_VCXR_MASK; + for (i = 0; i < XMIPICSISS_VCX_NUM_EVENTS; i++) { + if (!(vcxstatus & core->vcx_events[i].mask)) + continue; + core->vcx_events[i].counter++; + } + xcsi2rxss_write(core, XCSI_VCXR_OFFSET, vcxstatus); + } + } + + xcsi2rxss_write(core, XCSI_ISR_OFFSET, status); + + return IRQ_HANDLED; +} + +static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state) +{ + int i; + + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) + state->core.events[i].counter = 0; + + if (!state->core.en_vcx) + return; + + for (i = 0; i < XMIPICSISS_VCX_NUM_EVENTS; i++) + state->core.vcx_events[i].counter = 0; +} + +/** + * xcsi2rxss_log_counters - Print out the event counters. + * @state: Pointer to device state + * + */ +static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state) +{ + int i; + + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) { + if (state->core.events[i].counter > 0) + v4l2_info(&state->subdev, "%s events: %d\n", + state->core.events[i].name, + state->core.events[i].counter); + } + + if (!state->core.en_vcx) + return; + + for (i = 0; i < XMIPICSISS_VCX_NUM_EVENTS; i++) { + if (state->core.vcx_events[i].counter > 0) + v4l2_info(&state->subdev, + "VC %d Frame %s error vcx events: %d\n", + (i / 2) + XMIPICSISS_VCX_START, + i & 1 ? "Sync" : "Level", + state->core.vcx_events[i].counter); + } +} + +/** + * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver + * @sd: Pointer to V4L2 subdevice structure + * + * This function prints the current status of Xilinx MIPI CSI-2 + * + * Return: 0 on success + */ +static int xcsi2rxss_log_status(struct v4l2_subdev *sd) +{ + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + struct xcsi2rxss_core *core = &xcsi2rxss->core; + u32 reg, data, i, max_vc; + + mutex_lock(&xcsi2rxss->lock); + + xcsi2rxss_log_counters(xcsi2rxss); + + v4l2_info(sd, "***** Core Status *****\n"); + data = xcsi2rxss_read(core, XCSI_CSR_OFFSET); + v4l2_info(sd, "Short Packet FIFO Full = %s\n", + XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFOFULL_MASK)); + v4l2_info(sd, "Short Packet FIFO Not Empty = %s\n", + XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFONE_MASK)); + v4l2_info(sd, "Stream line buffer full = %s\n", + XCSI_GET_BITSET_STR(data, XCSI_CSR_SLBF_MASK)); + v4l2_info(sd, "Soft reset/Core disable in progress = %s\n", + XCSI_GET_BITSET_STR(data, XCSI_CSR_RIPCD_MASK)); + + /* Clk & Lane Info */ + v4l2_info(sd, "******** Clock Lane Info *********\n"); + data = xcsi2rxss_read(core, XCSI_CLKINFR_OFFSET); + v4l2_info(sd, "Clock Lane in Stop State = %s\n", + XCSI_GET_BITSET_STR(data, XCSI_CLKINFR_STOP_MASK)); + + v4l2_info(sd, "******** Data Lane Info *********\n"); + v4l2_info(sd, "Lane\tSoT Error\tSoT Sync Error\tStop State\n"); + reg = XCSI_L0INFR_OFFSET; + for (i = 0; i < 4; i++) { + data = xcsi2rxss_read(core, reg); + + v4l2_info(sd, "%d\t%s\t\t%s\t\t%s\n", + i, + XCSI_GET_BITSET_STR(data, XCSI_LXINFR_SOTERR_MASK), + XCSI_GET_BITSET_STR(data, XCSI_LXINFR_SOTSYNCERR_MASK), + XCSI_GET_BITSET_STR(data, XCSI_LXINFR_STOP_MASK)); + + reg += 4; + } + + /* Virtual Channel Image Information */ + v4l2_info(sd, "********** Virtual Channel Info ************\n"); + v4l2_info(sd, "VC\tLine Count\tByte Count\tData Type\n"); + if (core->en_vcx) + max_vc = XMIPICSISS_MAX_VCX; + else + max_vc = XMIPICSISS_MAX_VC; + + reg = XCSI_VC0INF1R_OFFSET; + for (i = 0; i < max_vc; i++) { + u32 line_count, byte_count, data_type; + char *datatypestr; + + /* Get line and byte count from VCXINFR1 Register */ + data = xcsi2rxss_read(core, reg); + byte_count = (data & XCSI_VCXINF1R_BYTECOUNT_MASK) >> + XCSI_VCXINF1R_BYTECOUNT_SHIFT; + line_count = (data & XCSI_VCXINF1R_LINECOUNT_MASK) >> + XCSI_VCXINF1R_LINECOUNT_SHIFT; + + /* Get data type from VCXINFR2 Register */ + reg += 4; + data = xcsi2rxss_read(core, reg); + data_type = (data & XCSI_VCXINF2R_DATATYPE_MASK) >> + XCSI_VCXINF2R_DATATYPE_SHIFT; + datatypestr = (char *)xcsi2rxss_pxlfmtdttostr(data_type); + + v4l2_info(sd, "%d\t%d\t\t%d\t\t%s\n", + i, line_count, byte_count, datatypestr); + + /* Move to next pair of VC Info registers */ + reg += 4; + } + + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/* + * xcsi2rxss_subscribe_event - Subscribe to the custom short packet + * receive event. + * @sd: V4L2 Sub device + * @fh: V4L2 File Handle + * @sub: Subcribe event structure + * + * There are two types of events to be subscribed. + * + * First is to register for receiving a short packet. + * The short packets received are queued up in a FIFO. + * On reception of a short packet, an event will be generated + * with the short packet contents copied to its data area. + * Application subscribed to this event will poll for POLLPRI. + * On getting the event, the app dequeues the event to get the short packet + * data. + * + * Second is to register for Short packet FIFO overflow + * In case the rate of receiving short packets is high and + * the short packet FIFO overflows, this event will be triggered. + * + * Return: 0 on success, errors otherwise + */ +static int xcsi2rxss_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + int ret; + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + + mutex_lock(&xcsi2rxss->lock); + + switch (sub->type) { + case V4L2_EVENT_XLNXCSIRX_SPKT: + case V4L2_EVENT_XLNXCSIRX_SPKT_OVF: + case V4L2_EVENT_XLNXCSIRX_SLBF: + ret = v4l2_event_subscribe(fh, sub, XCSI_MAX_SPKT, NULL); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&xcsi2rxss->lock); + + return ret; +} + +/** + * xcsi2rxss_unsubscribe_event - Unsubscribe from all events registered + * @sd: V4L2 Sub device + * @fh: V4L2 file handle + * @sub: pointer to Event unsubscription structure + * + * Return: zero on success, else a negative error code. + */ +static int xcsi2rxss_unsubscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + int ret = 0; + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + + mutex_lock(&xcsi2rxss->lock); + ret = v4l2_event_unsubscribe(fh, sub); + mutex_unlock(&xcsi2rxss->lock); + + return ret; +} + +/** + * xcsi2rxss_s_ctrl - This is used to set the Xilinx MIPI CSI-2 V4L2 controls + * @ctrl: V4L2 control to be set + * + * This function is used to set the V4L2 controls for the Xilinx MIPI + * CSI-2 Rx Subsystem. It is used to set the active lanes in the system. + * The event counters can be reset. + * + * Return: 0 on success, errors otherwise + */ +static int xcsi2rxss_s_ctrl(struct v4l2_ctrl *ctrl) +{ + int ret = 0; + u32 Timeout = XCSI_TIMEOUT_VAL; + u32 active_lanes = 1; + + struct xcsi2rxss_state *xcsi2rxss = + container_of(ctrl->handler, + struct xcsi2rxss_state, ctrl_handler); + struct xcsi2rxss_core *core = &xcsi2rxss->core; + + mutex_lock(&xcsi2rxss->lock); + + switch (ctrl->id) { + case V4L2_CID_XILINX_MIPICSISS_ACT_LANES: + /* + * This will be called only when "Enable Active Lanes" parameter + * is set in design + */ + xcsi2rxss_clr_and_set(core, XCSI_PCR_OFFSET, + XCSI_PCR_ACTLANES_MASK, ctrl->val - 1); + + /* + * If the core is enabled, wait for active lanes to be + * set. + * + * If core is disabled or there is no clock from DPHY Tx + * then the read back won't reflect the updated value + * as the PPI clock will not be present. + */ + + if (core->dphy_present) { + u32 dphyclkstatregoffset = core->dphy_offset + + XDPHY_CLKSTATREG_OFFSET; + + u32 dphyclkstat = + xcsi2rxss_read(core, dphyclkstatregoffset) & + XDPHY_CLKSTATREG_MODE_MASK; + + u32 coreenable = + xcsi2rxss_read(core, XCSI_CCR_OFFSET) & + XCSI_CCR_COREENB_MASK; + + char lpmstr[] = "Low Power"; + char hsmstr[] = "High Speed"; + char esmstr[] = "Escape"; + char *modestr; + + switch (dphyclkstat) { + case 0: + modestr = lpmstr; + break; + case 1: + modestr = hsmstr; + break; + case 2: + modestr = esmstr; + break; + default: + modestr = NULL; + break; + } + + dev_dbg(core->dev, "DPHY Clock Lane in %s mode\n", + modestr); + + if ((dphyclkstat == XDPHY_HI_SPEED_MODE) && + coreenable) { + + /* Wait for core to apply new active lanes */ + while (Timeout--) + udelay(1); + + active_lanes = + xcsi2rxss_read(core, XCSI_PCR_OFFSET); + active_lanes &= XCSI_PCR_ACTLANES_MASK; + active_lanes++; + + if (active_lanes != ctrl->val) { + dev_err(core->dev, "Failed to set active lanes!\n"); + ret = -EAGAIN; + } + } + } else { + dev_dbg(core->dev, "No read back as no DPHY present.\n"); + } + + dev_dbg(core->dev, "Set active lanes: requested = %d, active = %d\n", + ctrl->val, active_lanes); + break; + case V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS: + xcsi2rxss_reset_event_counters(xcsi2rxss); + break; + default: + break; + } + + mutex_unlock(&xcsi2rxss->lock); + + return ret; +} + +/** + * xcsi2rxss_g_volatile_ctrl - get the Xilinx MIPI CSI-2 Rx controls + * @ctrl: Pointer to V4L2 control + * + * This is used to get the number of frames received by the Xilinx + * MIPI CSI-2 Rx. + * + * Return: 0 on success, errors otherwise + */ +static int xcsi2rxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + int ret = 0; + struct xcsi2rxss_state *xcsi2rxss = + container_of(ctrl->handler, + struct xcsi2rxss_state, ctrl_handler); + + mutex_lock(&xcsi2rxss->lock); + + switch (ctrl->id) { + case V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER: + ctrl->val = xcsi2rxss->core.events[0].counter; + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&xcsi2rxss->lock); + + return ret; +} + +static int xcsi2rxss_start_stream(struct xcsi2rxss_state *xcsi2rxss) +{ + int ret; + + xcsi2rxss_enable(&xcsi2rxss->core, true); + + ret = xcsi2rxss_reset(&xcsi2rxss->core); + if (ret < 0) + return ret; + + xcsi2rxss_interrupts_enable(&xcsi2rxss->core, true); + + return 0; +} + +static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *xcsi2rxss) +{ + xcsi2rxss_interrupts_enable(&xcsi2rxss->core, false); + xcsi2rxss_enable(&xcsi2rxss->core, false); +} + +/** + * xcsi2rxss_s_stream - It is used to start/stop the streaming. + * @sd: V4L2 Sub device + * @enable: Flag (True / False) + * + * This function controls the start or stop of streaming for the + * Xilinx MIPI CSI-2 Rx Subsystem provided the device isn't in + * suspended state. + * + * Return: 0 on success, errors otherwise + */ +static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable) +{ + int ret = 0; + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + + mutex_lock(&xcsi2rxss->lock); + + if (xcsi2rxss->suspended) { + ret = -EBUSY; + goto unlock; + } + + if (enable) { + if (!xcsi2rxss->streaming) { + /* reset the event counters */ + xcsi2rxss_reset_event_counters(xcsi2rxss); + + ret = xcsi2rxss_start_stream(xcsi2rxss); + if (ret == 0) + xcsi2rxss->streaming = true; + } + } else { + if (xcsi2rxss->streaming) { + xcsi2rxss_stop_stream(xcsi2rxss); + xcsi2rxss->streaming = false; + } + } +unlock: + mutex_unlock(&xcsi2rxss->lock); + return ret; +} + +static struct v4l2_mbus_framefmt * +__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xcsi2rxss->formats[pad]; + default: + return NULL; + } +} + +/** + * xcsi2rxss_get_format - Get the pad format + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * @fmt: Pointer to pad level media bus format + * + * This function is used to get the pad format information. + * + * Return: 0 on success + */ +static int xcsi2rxss_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + + mutex_lock(&xcsi2rxss->lock); + fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg, + fmt->pad, fmt->which); + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/** + * xcsi2rxss_set_format - This is used to set the pad format + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * @fmt: Pointer to pad level media bus format + * + * This function is used to set the pad format. + * Since the pad format is fixed in hardware, it can't be + * modified on run time. So when a format set is requested by + * application, all parameters except the format type is + * saved for the pad and the original pad format is sent + * back to the application. + * + * Return: 0 on success + */ +static int xcsi2rxss_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *__format; + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + struct xcsi2rxss_core *core = &xcsi2rxss->core; + u32 code; + + mutex_lock(&xcsi2rxss->lock); + + /* + * Only the format->code parameter matters for CSI as the + * CSI format cannot be changed at runtime. + * Ensure that format to set is copied to over to CSI pad format + */ + __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg, + fmt->pad, fmt->which); + + /* Save the pad format code */ + code = __format->code; + + /* If the bayer pattern to be set is SXXXX8 then only 1x8 type + * is supported and core's data type doesn't matter. + * In case the bayer pattern being set is SXXX10 then only + * 1x10 type are supported and core should be configured for RAW10. + * In case the bayer pattern being set is SXXX12 then only + * 1x12 type are supported and core should be configured for RAW12. + * + * Otherwise don't allow change. + */ + if (((fmt->format.code == MEDIA_BUS_FMT_SBGGR8_1X8) || + (fmt->format.code == MEDIA_BUS_FMT_SGBRG8_1X8) || + (fmt->format.code == MEDIA_BUS_FMT_SGRBG8_1X8) || + (fmt->format.code == MEDIA_BUS_FMT_SRGGB8_1X8)) + || ((core->datatype == MIPI_CSI_DT_RAW_10) && + ((fmt->format.code == MEDIA_BUS_FMT_SBGGR10_1X10) || + (fmt->format.code == MEDIA_BUS_FMT_SGBRG10_1X10) || + (fmt->format.code == MEDIA_BUS_FMT_SGRBG10_1X10) || + (fmt->format.code == MEDIA_BUS_FMT_SRGGB10_1X10))) + || ((core->datatype == MIPI_CSI_DT_RAW_12) && + ((fmt->format.code == MEDIA_BUS_FMT_SBGGR12_1X12) || + (fmt->format.code == MEDIA_BUS_FMT_SGBRG12_1X12) || + (fmt->format.code == MEDIA_BUS_FMT_SGRBG12_1X12) || + (fmt->format.code == MEDIA_BUS_FMT_SRGGB12_1X12))) || + ((core->datatype == MIPI_CSI_DT_RAW_16) && + ((fmt->format.code == MEDIA_BUS_FMT_SBGGR16_1X16) || + (fmt->format.code == MEDIA_BUS_FMT_SGBRG16_1X16) || + (fmt->format.code == MEDIA_BUS_FMT_SGRBG16_1X16) || + (fmt->format.code == MEDIA_BUS_FMT_SRGGB16_1X16)))) + + /* Copy over the format to be set */ + *__format = fmt->format; + else { + /* Restore the original pad format code */ + fmt->format.code = code; + __format->code = code; + __format->width = fmt->format.width; + __format->height = fmt->format.height; + } + + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/** + * xcsi2rxss_open - Called on v4l2_open() + * @sd: Pointer to V4L2 sub device structure + * @fh: Pointer to V4L2 File handle + * + * This function is called on v4l2_open(). It sets the default format + * for both pads. + * + * Return: 0 on success + */ +static int xcsi2rxss_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd); + + format = v4l2_subdev_get_try_format(sd, fh->pad, 0); + *format = xcsi2rxss->default_format; + + format = v4l2_subdev_get_try_format(sd, fh->pad, 1); + *format = xcsi2rxss->default_format; + + return 0; +} + +static int xcsi2rxss_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + return 0; +} + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static const struct media_entity_operations xcsi2rxss_media_ops = { + .link_validate = v4l2_subdev_link_validate +}; + +static const struct v4l2_ctrl_ops xcsi2rxss_ctrl_ops = { + .g_volatile_ctrl = xcsi2rxss_g_volatile_ctrl, + .s_ctrl = xcsi2rxss_s_ctrl +}; + +static struct v4l2_ctrl_config xcsi2rxss_ctrls[] = { + { + .ops = &xcsi2rxss_ctrl_ops, + .id = V4L2_CID_XILINX_MIPICSISS_ACT_LANES, + .name = "MIPI CSI2 Rx Subsystem: Active Lanes", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 4, + .step = 1, + .def = 1, + }, { + .ops = &xcsi2rxss_ctrl_ops, + .id = V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER, + .name = "MIPI CSI2 Rx Subsystem: Frames Received Counter", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 0xFFFFFFFF, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, { + .ops = &xcsi2rxss_ctrl_ops, + .id = V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS, + .name = "MIPI CSI2 Rx Subsystem: Reset Counters", + .type = V4L2_CTRL_TYPE_BUTTON, + .min = 0, + .max = 1, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_WRITE_ONLY, + } +}; + +static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = { + .log_status = xcsi2rxss_log_status, + .subscribe_event = xcsi2rxss_subscribe_event, + .unsubscribe_event = xcsi2rxss_unsubscribe_event +}; + +static struct v4l2_subdev_video_ops xcsi2rxss_video_ops = { + .s_stream = xcsi2rxss_s_stream +}; + +static struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = { + .get_fmt = xcsi2rxss_get_format, + .set_fmt = xcsi2rxss_set_format, +}; + +static struct v4l2_subdev_ops xcsi2rxss_ops = { + .core = &xcsi2rxss_core_ops, + .video = &xcsi2rxss_video_ops, + .pad = &xcsi2rxss_pad_ops +}; + +static const struct v4l2_subdev_internal_ops xcsi2rxss_internal_ops = { + .open = xcsi2rxss_open, + .close = xcsi2rxss_close +}; + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +/* + * xcsi2rxss_pm_suspend - Function called on Power Suspend + * @dev: Pointer to device structure + * + * On power suspend the CSI-2 Core is disabled if the device isn't + * in suspended state and is streaming. + * + * Return: 0 on success + */ +static int __maybe_unused xcsi2rxss_pm_suspend(struct device *dev) +{ + struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev); + + mutex_lock(&xcsi2rxss->lock); + + if (!xcsi2rxss->suspended && xcsi2rxss->streaming) + xcsi2rxss_clr(&xcsi2rxss->core, + XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK); + + xcsi2rxss->suspended = true; + + mutex_unlock(&xcsi2rxss->lock); + + return 0; +} + +/* + * xcsi2rxss_pm_resume - Function called on Power Resume + * @dev: Pointer to device structure + * + * On power resume the CSI-2 Core is enabled when it is in suspended state + * and prior to entering suspended state it was streaming. + * + * Return: 0 on success + */ +static int __maybe_unused xcsi2rxss_pm_resume(struct device *dev) +{ + struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev); + + mutex_lock(&xcsi2rxss->lock); + + if ((xcsi2rxss->suspended) && (xcsi2rxss->streaming)) + xcsi2rxss_set(&xcsi2rxss->core, + XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK); + + xcsi2rxss->suspended = false; + + mutex_unlock(&xcsi2rxss->lock); + return 0; +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss) +{ + struct device_node *node = xcsi2rxss->core.dev->of_node; + struct device_node *ports = NULL; + struct device_node *port = NULL; + unsigned int nports = 0; + struct xcsi2rxss_core *core = &xcsi2rxss->core; + int ret; + bool iic_present; + + if (core->cfg->flags & XCSI_CLK_PROP) { + core->lite_aclk = devm_clk_get(core->dev, "lite_aclk"); + if (IS_ERR(core->lite_aclk)) { + ret = PTR_ERR(core->lite_aclk); + dev_err(core->dev, "failed to get lite_aclk (%d)\n", + ret); + return ret; + } + + core->video_aclk = devm_clk_get(core->dev, "video_aclk"); + if (IS_ERR(core->video_aclk)) { + ret = PTR_ERR(core->video_aclk); + dev_err(core->dev, "failed to get video_aclk (%d)\n", + ret); + return ret; + } + + core->dphy_clk_200M = devm_clk_get(core->dev, "dphy_clk_200M"); + if (IS_ERR(core->dphy_clk_200M)) { + ret = PTR_ERR(core->dphy_clk_200M); + dev_err(core->dev, "failed to get dphy_clk_200M (%d)\n", + ret); + return ret; + } + } else { + dev_info(core->dev, "assuming all required clocks are enabled!\n"); + } + + core->dphy_present = of_property_read_bool(node, "xlnx,dphy-present"); + dev_dbg(core->dev, "DPHY present property = %s\n", + core->dphy_present ? "Present" : "Absent"); + + iic_present = of_property_read_bool(node, "xlnx,iic-present"); + dev_dbg(core->dev, "IIC present property = %s\n", + iic_present ? "Present" : "Absent"); + + if (core->dphy_present) { + if (iic_present) + core->dphy_offset = 0x20000; + else + core->dphy_offset = 0x10000; + } + + ret = of_property_read_u32(node, "xlnx,max-lanes", + &core->max_num_lanes); + if (ret < 0) { + dev_err(core->dev, "missing xlnx,max-lanes property\n"); + return ret; + } + + if ((core->max_num_lanes > 4) || (core->max_num_lanes < 1)) { + dev_err(core->dev, "%d max lanes : invalid xlnx,max-lanes property\n", + core->max_num_lanes); + return -EINVAL; + } + + core->en_vcx = of_property_read_bool(node, "xlnx,en-vcx"); + + ret = of_property_read_u32(node, "xlnx,vc", &core->vc); + if (ret < 0) { + dev_err(core->dev, "missing xlnx,vc property\n"); + return ret; + } + if ((core->vc > XMIPICSISS_MAX_VC && !core->en_vcx) || + (core->vc > XMIPICSISS_MAX_VCX && core->en_vcx)) { + dev_err(core->dev, "invalid virtual channel property value.\n"); + return -EINVAL; + } + + core->enable_active_lanes = + of_property_read_bool(node, "xlnx,en-active-lanes"); + dev_dbg(core->dev, "Enable active lanes property = %s\n", + core->enable_active_lanes ? "Present" : "Absent"); + + ret = of_property_read_string(node, "xlnx,csi-pxl-format", + &core->pxlformat); + if (ret < 0) { + dev_err(core->dev, "missing xlnx,csi-pxl-format property\n"); + return ret; + } + + core->datatype = xcsi2rxss_pxlfmtstrtodt(core->pxlformat); + if ((core->datatype < MIPI_CSI_DT_YUV_420_8B) || + (core->datatype > MIPI_CSI_DT_RAW_20)) { + dev_err(core->dev, "Invalid xlnx,csi-pxl-format string\n"); + return -EINVAL; + } + + core->vfb = of_property_read_bool(node, "xlnx,vfb"); + dev_dbg(core->dev, "Video Format Bridge property = %s\n", + core->vfb ? "Present" : "Absent"); + + if (core->vfb) { + ret = of_property_read_u32(node, "xlnx,ppc", &core->ppc); + if ((ret < 0) || !((core->ppc == 1) || + (core->ppc == 2) || (core->ppc == 4))) { + dev_err(core->dev, "Invalid xlnx,ppc property ret = %d ppc = %d\n", + ret, core->ppc); + return -EINVAL; + } + } + + ports = of_get_child_by_name(node, "ports"); + if (ports == NULL) + ports = node; + + for_each_child_of_node(ports, port) { + int ret; + const struct xvip_video_format *format; + struct device_node *endpoint; + struct v4l2_fwnode_endpoint v4lendpoint; + + if (!port->name || of_node_cmp(port->name, "port")) + continue; + + /* + * Currently only a subset of VFB enabled formats present in + * xvip are supported in the driver. + * + * If the VFB is disabled, the pixels per clock don't matter. + * The data width is either 32 or 64 bit as selected in design. + * + * For e.g. If Data Type is RGB888, VFB is disabled and + * data width is 32 bits. + * + * Clk Cycle | Byte 0 | Byte 1 | Byte 2 | Byte 3 + * -----------+----------+----------+----------+---------- + * 1 | B0 | G0 | R0 | B1 + * 2 | G1 | R1 | B2 | G2 + * 3 | R2 | B3 | G3 | R3 + */ + format = xvip_of_get_format(port); + if (IS_ERR(format)) { + dev_err(core->dev, "invalid format in DT"); + return PTR_ERR(format); + } + + if (core->vfb && + (format->vf_code != XVIP_VF_YUV_422) && + (format->vf_code != XVIP_VF_RBG) && + (format->vf_code != XVIP_VF_MONO_SENSOR)) { + dev_err(core->dev, "Invalid UG934 video format set.\n"); + return -EINVAL; + } + + /* Get and check the format description */ + if (!xcsi2rxss->vip_format) { + xcsi2rxss->vip_format = format; + } else if (xcsi2rxss->vip_format != format) { + dev_err(core->dev, "in/out format mismatch in DT"); + return -EINVAL; + } + + endpoint = of_get_next_child(port, NULL); + if (!endpoint) { + dev_err(core->dev, "No port at\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), + &v4lendpoint); + if (ret) { + of_node_put(endpoint); + return ret; + } + + of_node_put(endpoint); + dev_dbg(core->dev, "%s : port %d bus type = %d\n", + __func__, nports, v4lendpoint.bus_type); + + if (v4lendpoint.bus_type == V4L2_MBUS_CSI2_DPHY) { + dev_dbg(core->dev, "%s : base.port = %d base.id = %d\n", + __func__, + v4lendpoint.base.port, + v4lendpoint.base.id); + + dev_dbg(core->dev, "%s : mipi number lanes = %d\n", + __func__, + v4lendpoint.bus.mipi_csi2.num_data_lanes); + } else { + dev_dbg(core->dev, "%s : Not a CSI2 bus\n", __func__); + } + + /* Count the number of ports. */ + nports++; + } + + if (nports != 2) { + dev_err(core->dev, "invalid number of ports %u\n", nports); + return -EINVAL; + } + xcsi2rxss->npads = nports; + + /*Register interrupt handler */ + core->irq = irq_of_parse_and_map(node, 0); + + ret = devm_request_irq(core->dev, core->irq, xcsi2rxss_irq_handler, + IRQF_SHARED, "xilinx-csi2rxss", xcsi2rxss); + if (ret) { + dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n", + ret); + return ret; + } + + return 0; +} + +static int xcsi2rxss_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct xcsi2rxss_state *xcsi2rxss; + struct resource *res; + const struct of_device_id *match; + struct device_node *node = pdev->dev.of_node; + u32 i; + int ret; + int num_ctrls; + + xcsi2rxss = devm_kzalloc(&pdev->dev, sizeof(*xcsi2rxss), GFP_KERNEL); + if (!xcsi2rxss) + return -ENOMEM; + + mutex_init(&xcsi2rxss->lock); + + xcsi2rxss->core.dev = &pdev->dev; + + match = of_match_node(xcsi2rxss_of_id_table, node); + if (!match) + return -ENODEV; + + xcsi2rxss->core.cfg = match->data; + + ret = xcsi2rxss_parse_of(xcsi2rxss); + if (ret < 0) + return ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xcsi2rxss->core.iomem = devm_ioremap_resource(xcsi2rxss->core.dev, res); + if (IS_ERR(xcsi2rxss->core.iomem)) + return PTR_ERR(xcsi2rxss->core.iomem); + + if (xcsi2rxss->core.cfg->flags & XCSI_CLK_PROP) { + unsigned long rate; + + ret = clk_prepare_enable(xcsi2rxss->core.lite_aclk); + if (ret) { + dev_err(&pdev->dev, "failed to enable lite_aclk (%d)\n", + ret); + goto clk_err; + } + + ret = clk_prepare_enable(xcsi2rxss->core.video_aclk); + if (ret) { + dev_err(&pdev->dev, "failed to enable video_aclk (%d)\n", + ret); + goto video_aclk_err; + } + + ret = clk_prepare_enable(xcsi2rxss->core.dphy_clk_200M); + if (ret) { + dev_err(&pdev->dev, "failed to enable dphy clk (%d)\n", + ret); + goto dphy_clk_err; + } + + ret = clk_set_rate(xcsi2rxss->core.dphy_clk_200M, + XCSI_DPHY_CLK_REQ); + if (ret) { + dev_err(&pdev->dev, "failed to set dphy clk rate (%d)\n", + ret); + + goto all_clk_err; + } + + rate = clk_get_rate(xcsi2rxss->core.dphy_clk_200M); + if (rate < XCSI_DPHY_CLK_MIN && rate > XCSI_DPHY_CLK_MAX) { + dev_err(&pdev->dev, "Err DPHY Clock = %lu\n", + rate); + ret = -EINVAL; + goto all_clk_err; + } + } + + /* + * Reset and initialize the core. + */ + xcsi2rxss_reset(&xcsi2rxss->core); + + xcsi2rxss->core.events = (struct xcsi2rxss_event *)&xcsi2rxss_events; + + if (xcsi2rxss->core.en_vcx) { + u32 alloc_size; + + alloc_size = sizeof(struct xcsi2rxss_event) * + XMIPICSISS_VCX_NUM_EVENTS; + xcsi2rxss->core.vcx_events = devm_kzalloc(&pdev->dev, + alloc_size, + GFP_KERNEL); + if (!xcsi2rxss->core.vcx_events) { + mutex_destroy(&xcsi2rxss->lock); + return -ENOMEM; + } + + for (i = 0; i < XMIPICSISS_VCX_NUM_EVENTS; i++) + xcsi2rxss->core.vcx_events[i].mask = 1 << i; + } + + /* Initialize V4L2 subdevice and media entity */ + xcsi2rxss->pads[0].flags = MEDIA_PAD_FL_SOURCE; + xcsi2rxss->pads[1].flags = MEDIA_PAD_FL_SINK; + + /* Initialize the default format */ + memset(&xcsi2rxss->default_format, 0, + sizeof(xcsi2rxss->default_format)); + xcsi2rxss->default_format.code = xcsi2rxss->vip_format->code; + xcsi2rxss->default_format.field = V4L2_FIELD_NONE; + xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB; + xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH; + xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT; + + xcsi2rxss->formats[0] = xcsi2rxss->default_format; + xcsi2rxss->formats[1] = xcsi2rxss->default_format; + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xcsi2rxss->subdev; + v4l2_subdev_init(subdev, &xcsi2rxss_ops); + + subdev->dev = &pdev->dev; + subdev->internal_ops = &xcsi2rxss_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + + subdev->entity.ops = &xcsi2rxss_media_ops; + + v4l2_set_subdevdata(subdev, xcsi2rxss); + + ret = media_entity_pads_init(&subdev->entity, 2, xcsi2rxss->pads); + if (ret < 0) + goto error; + + /* + * In case the Enable Active Lanes config parameter is not set, + * dynamic lane reconfiguration is not allowed. + * So V4L2_CID_XILINX_MIPICSISS_ACT_LANES ctrl will not be registered. + * Accordingly allocate the number of controls + */ + num_ctrls = ARRAY_SIZE(xcsi2rxss_ctrls); + + if (!xcsi2rxss->core.enable_active_lanes) + num_ctrls--; + + dev_dbg(xcsi2rxss->core.dev, "# of ctrls = %d\n", num_ctrls); + + v4l2_ctrl_handler_init(&xcsi2rxss->ctrl_handler, num_ctrls); + + for (i = 0; i < ARRAY_SIZE(xcsi2rxss_ctrls); i++) { + struct v4l2_ctrl *ctrl; + + if (xcsi2rxss_ctrls[i].id == + V4L2_CID_XILINX_MIPICSISS_ACT_LANES) { + + if (xcsi2rxss->core.enable_active_lanes) { + xcsi2rxss_ctrls[i].max = + xcsi2rxss->core.max_num_lanes; + } else { + /* Don't register control */ + dev_dbg(xcsi2rxss->core.dev, + "Skip active lane control\n"); + continue; + } + } + + dev_dbg(xcsi2rxss->core.dev, "%d ctrl = 0x%x\n", + i, xcsi2rxss_ctrls[i].id); + ctrl = v4l2_ctrl_new_custom(&xcsi2rxss->ctrl_handler, + &xcsi2rxss_ctrls[i], NULL); + if (!ctrl) { + dev_err(xcsi2rxss->core.dev, "Failed for %s ctrl\n", + xcsi2rxss_ctrls[i].name); + goto error; + } + } + + dev_dbg(xcsi2rxss->core.dev, "# v4l2 ctrls registered = %d\n", i - 1); + + if (xcsi2rxss->ctrl_handler.error) { + dev_err(&pdev->dev, "failed to add controls\n"); + ret = xcsi2rxss->ctrl_handler.error; + goto error; + } + + subdev->ctrl_handler = &xcsi2rxss->ctrl_handler; + + ret = v4l2_ctrl_handler_setup(&xcsi2rxss->ctrl_handler); + if (ret < 0) { + dev_err(&pdev->dev, "failed to set controls\n"); + goto error; + } + + platform_set_drvdata(pdev, xcsi2rxss); + + dev_info(xcsi2rxss->core.dev, "Xilinx CSI2 Rx Subsystem device found!\n"); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + /* default states for streaming and suspend */ + xcsi2rxss->streaming = false; + xcsi2rxss->suspended = false; + return 0; + +error: + v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler); + media_entity_cleanup(&subdev->entity); + mutex_destroy(&xcsi2rxss->lock); + +all_clk_err: + clk_disable_unprepare(xcsi2rxss->core.dphy_clk_200M); +dphy_clk_err: + clk_disable_unprepare(xcsi2rxss->core.video_aclk); +video_aclk_err: + clk_disable_unprepare(xcsi2rxss->core.lite_aclk); +clk_err: + return ret; +} + +static int xcsi2rxss_remove(struct platform_device *pdev) +{ + struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xcsi2rxss->subdev; + + v4l2_async_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler); + media_entity_cleanup(&subdev->entity); + mutex_destroy(&xcsi2rxss->lock); + clk_disable_unprepare(xcsi2rxss->core.dphy_clk_200M); + clk_disable_unprepare(xcsi2rxss->core.video_aclk); + clk_disable_unprepare(xcsi2rxss->core.lite_aclk); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xcsi2rxss_pm_ops, + xcsi2rxss_pm_suspend, xcsi2rxss_pm_resume); + +static struct platform_driver xcsi2rxss_driver = { + .driver = { + .name = "xilinx-csi2rxss", + .pm = &xcsi2rxss_pm_ops, + .of_match_table = xcsi2rxss_of_id_table, + }, + .probe = xcsi2rxss_probe, + .remove = xcsi2rxss_remove, +}; + +module_platform_driver(xcsi2rxss_driver); + +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>"); +MODULE_DESCRIPTION("Xilinx MIPI CSI2 Rx Subsystem Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-demosaic.c b/drivers/media/platform/xilinx/xilinx-demosaic.c new file mode 100644 index 000000000000..a519c2c9719b --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-demosaic.c @@ -0,0 +1,418 @@ +/* + * Xilinx Video Demosaic IP + * + * Copyright (C) 2017 Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-vip.h" + +#define XDEMOSAIC_AP_CTRL (0x00) +#define XDEMOSAIC_WIDTH (0x10) +#define XDEMOSAIC_HEIGHT (0x18) +#define XDEMOSAIC_INPUT_BAYER_FORMAT (0x28) + +#define XDEMOSAIC_MIN_HEIGHT (64) +#define XDEMOSAIC_MAX_HEIGHT (4320) +#define XDEMOSAIC_DEF_HEIGHT (720) +#define XDEMOSAIC_MIN_WIDTH (64) +#define XDEMOSAIC_MAX_WIDTH (8192) +#define XDEMOSAIC_DEF_WIDTH (1280) + +#define XDEMOSAIC_RESET_DEASSERT (0) +#define XDEMOSAIC_RESET_ASSERT (1) +#define XDEMOSAIC_START BIT(0) +#define XDEMOSAIC_AUTO_RESTART BIT(7) +#define XDEMOSAIC_STREAM_ON (XDEMOSAIC_AUTO_RESTART | XDEMOSAIC_START) + +enum xdmsc_bayer_format { + XDEMOSAIC_RGGB = 0, + XDEMOSAIC_GRBG, + XDEMOSAIC_GBRG, + XDEMOSAIC_BGGR, +}; + +struct xdmsc_dev { + struct xvip_device xvip; + struct media_pad pads[2]; + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + + enum xdmsc_bayer_format bayer_fmt; + struct gpio_desc *rst_gpio; + u32 max_width; + u32 max_height; +}; + +static inline u32 xdmsc_read(struct xdmsc_dev *xdmsc, u32 reg) +{ + u32 data; + + data = xvip_read(&xdmsc->xvip, reg); + dev_dbg(xdmsc->xvip.dev, + "Reading 0x%x from reg offset 0x%x", data, reg); + return data; +} + +static inline void xdmsc_write(struct xdmsc_dev *xdmsc, u32 reg, u32 data) +{ + xvip_write(&xdmsc->xvip, reg, data); + dev_dbg(xdmsc->xvip.dev, + "Writing 0x%x to reg offset 0x%x", data, reg); +#ifdef DEBUG + if (xdmsc_read(xdmsc, reg) != data) + dev_err(xdmsc->xvip.dev, + "Wrote 0x%x does not match read back", data); +#endif +} + +static inline struct xdmsc_dev *to_xdmsc(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xdmsc_dev, xvip.subdev); +} + +static struct v4l2_mbus_framefmt +*__xdmsc_get_pad_format(struct xdmsc_dev *xdmsc, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xdmsc->xvip.subdev, + cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xdmsc->formats[pad]; + default: + return NULL; + } +} + +static int xdmsc_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xdmsc_dev *xdmsc = to_xdmsc(subdev); + + if (!enable) { + dev_dbg(xdmsc->xvip.dev, "%s : Off", __func__); + gpiod_set_value_cansleep(xdmsc->rst_gpio, + XDEMOSAIC_RESET_ASSERT); + gpiod_set_value_cansleep(xdmsc->rst_gpio, + XDEMOSAIC_RESET_DEASSERT); + return 0; + } + + xdmsc_write(xdmsc, XDEMOSAIC_WIDTH, + xdmsc->formats[XVIP_PAD_SINK].width); + xdmsc_write(xdmsc, XDEMOSAIC_HEIGHT, + xdmsc->formats[XVIP_PAD_SINK].height); + xdmsc_write(xdmsc, XDEMOSAIC_INPUT_BAYER_FORMAT, xdmsc->bayer_fmt); + + /* Start Demosaic Video IP */ + xdmsc_write(xdmsc, XDEMOSAIC_AP_CTRL, XDEMOSAIC_STREAM_ON); + return 0; +} + +static const struct v4l2_subdev_video_ops xdmsc_video_ops = { + .s_stream = xdmsc_s_stream, +}; + +static int xdmsc_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xdmsc_dev *xdmsc = to_xdmsc(subdev); + + fmt->format = *__xdmsc_get_pad_format(xdmsc, cfg, fmt->pad, fmt->which); + return 0; +} + +static bool +xdmsc_is_format_bayer(struct xdmsc_dev *xdmsc, u32 code) +{ + switch (code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SRGGB16_1X16: + xdmsc->bayer_fmt = XDEMOSAIC_RGGB; + break; + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGRBG16_1X16: + xdmsc->bayer_fmt = XDEMOSAIC_GRBG; + break; + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGBRG16_1X16: + xdmsc->bayer_fmt = XDEMOSAIC_GBRG; + break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SBGGR16_1X16: + xdmsc->bayer_fmt = XDEMOSAIC_BGGR; + break; + default: + dev_dbg(xdmsc->xvip.dev, "Unsupported format for Sink Pad"); + return false; + } + return true; +} + +static int xdmsc_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xdmsc_dev *xdmsc = to_xdmsc(subdev); + struct v4l2_mbus_framefmt *__format; + + __format = __xdmsc_get_pad_format(xdmsc, cfg, fmt->pad, fmt->which); + *__format = fmt->format; + + __format->width = clamp_t(unsigned int, fmt->format.width, + XDEMOSAIC_MIN_WIDTH, xdmsc->max_width); + __format->height = clamp_t(unsigned int, fmt->format.height, + XDEMOSAIC_MIN_HEIGHT, xdmsc->max_height); + + if (fmt->pad == XVIP_PAD_SOURCE) { + if (__format->code != MEDIA_BUS_FMT_RBG888_1X24 && + __format->code != MEDIA_BUS_FMT_RBG101010_1X30 && + __format->code != MEDIA_BUS_FMT_RBG121212_1X36 && + __format->code != MEDIA_BUS_FMT_RBG161616_1X48) { + dev_dbg(xdmsc->xvip.dev, + "%s : Unsupported source media bus code format", + __func__); + __format->code = MEDIA_BUS_FMT_RBG888_1X24; + } + } + + if (fmt->pad == XVIP_PAD_SINK) { + if (!xdmsc_is_format_bayer(xdmsc, __format->code)) { + dev_dbg(xdmsc->xvip.dev, + "Unsupported Sink Pad Media format, defaulting to RGGB"); + __format->code = MEDIA_BUS_FMT_SRGGB8_1X8; + } + } + + fmt->format = *__format; + return 0; +} + +static int xdmsc_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct xdmsc_dev *xdmsc = to_xdmsc(subdev); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK); + *format = xdmsc->default_formats[XVIP_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE); + *format = xdmsc->default_formats[XVIP_PAD_SOURCE]; + return 0; +} + +static int xdmsc_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static const struct v4l2_subdev_internal_ops xdmsc_internal_ops = { + .open = xdmsc_open, + .close = xdmsc_close, +}; + +static const struct v4l2_subdev_pad_ops xdmsc_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xvip_enum_frame_size, + .get_fmt = xdmsc_get_format, + .set_fmt = xdmsc_set_format, +}; + +static const struct v4l2_subdev_ops xdmsc_ops = { + .video = &xdmsc_video_ops, + .pad = &xdmsc_pad_ops, +}; + +static const struct media_entity_operations xdmsc_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int xdmsc_parse_of(struct xdmsc_dev *xdmsc) +{ + struct device *dev = xdmsc->xvip.dev; + struct device_node *node = dev->of_node; + struct device_node *ports; + struct device_node *port; + u32 port_id = 0; + int rval; + + rval = of_property_read_u32(node, "xlnx,max-height", + &xdmsc->max_height); + if (rval < 0) { + dev_err(dev, "missing xlnx,max-height property!"); + return -EINVAL; + } else if (xdmsc->max_height > XDEMOSAIC_MAX_HEIGHT || + xdmsc->max_height < XDEMOSAIC_MIN_HEIGHT) { + dev_err(dev, "Invalid height in dt"); + return -EINVAL; + } + + rval = of_property_read_u32(node, "xlnx,max-width", + &xdmsc->max_width); + if (rval < 0) { + dev_err(dev, "missing xlnx,max-width property!"); + return -EINVAL; + } else if (xdmsc->max_width > XDEMOSAIC_MAX_WIDTH || + xdmsc->max_width < XDEMOSAIC_MIN_WIDTH) { + dev_err(dev, "Invalid width in dt"); + return -EINVAL; + } + + ports = of_get_child_by_name(node, "ports"); + if (!ports) + ports = node; + /* Get the format description for each pad */ + for_each_child_of_node(ports, port) { + if (port->name && (of_node_cmp(port->name, "port") == 0)) { + rval = of_property_read_u32(port, "reg", &port_id); + if (rval < 0) { + dev_err(dev, "No reg in DT"); + return rval; + } + + if (port_id != 0 && port_id != 1) { + dev_err(dev, "Invalid reg in DT"); + return -EINVAL; + } + } + } + + xdmsc->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(xdmsc->rst_gpio)) { + if (PTR_ERR(xdmsc->rst_gpio) != -EPROBE_DEFER) + dev_err(dev, "Reset GPIO not setup in DT"); + return PTR_ERR(xdmsc->rst_gpio); + } + return 0; +} + +static int xdmsc_probe(struct platform_device *pdev) +{ + struct xdmsc_dev *xdmsc; + struct v4l2_subdev *subdev; + struct v4l2_mbus_framefmt *def_fmt; + int rval; + + xdmsc = devm_kzalloc(&pdev->dev, sizeof(*xdmsc), GFP_KERNEL); + if (!xdmsc) + return -ENOMEM; + xdmsc->xvip.dev = &pdev->dev; + rval = xdmsc_parse_of(xdmsc); + if (rval < 0) + return rval; + rval = xvip_init_resources(&xdmsc->xvip); + + /* Reset Demosaic IP */ + gpiod_set_value_cansleep(xdmsc->rst_gpio, + XDEMOSAIC_RESET_DEASSERT); + + /* Init V4L2 subdev */ + subdev = &xdmsc->xvip.subdev; + v4l2_subdev_init(subdev, &xdmsc_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xdmsc_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Default Formats Initialization */ + def_fmt = &xdmsc->default_formats[XVIP_PAD_SINK]; + def_fmt->field = V4L2_FIELD_NONE; + def_fmt->colorspace = V4L2_COLORSPACE_SRGB; + def_fmt->width = XDEMOSAIC_DEF_WIDTH; + def_fmt->height = XDEMOSAIC_DEF_HEIGHT; + + /* + * Sink Pad can be any Bayer format. + * Default Sink Pad format is RGGB. + */ + def_fmt->code = MEDIA_BUS_FMT_SRGGB8_1X8; + xdmsc->formats[XVIP_PAD_SINK] = *def_fmt; + + def_fmt = &xdmsc->default_formats[XVIP_PAD_SOURCE]; + *def_fmt = xdmsc->default_formats[XVIP_PAD_SINK]; + + /* Source Pad has a fixed media bus format of RGB */ + def_fmt->code = MEDIA_BUS_FMT_RBG888_1X24; + xdmsc->formats[XVIP_PAD_SOURCE] = *def_fmt; + + xdmsc->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xdmsc->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + /* Init Media Entity */ + subdev->entity.ops = &xdmsc_media_ops; + rval = media_entity_pads_init(&subdev->entity, 2, xdmsc->pads); + if (rval < 0) + goto media_error; + + platform_set_drvdata(pdev, xdmsc); + rval = v4l2_async_register_subdev(subdev); + if (rval < 0) { + dev_err(&pdev->dev, "failed to register subdev"); + goto v4l2_subdev_error; + } + dev_info(&pdev->dev, + "Xilinx Video Demosaic Probe Successful"); + return 0; + +v4l2_subdev_error: + media_entity_cleanup(&subdev->entity); +media_error: + xvip_cleanup_resources(&xdmsc->xvip); + return rval; +} + +static int xdmsc_remove(struct platform_device *pdev) +{ + struct xdmsc_dev *xdmsc = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xdmsc->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + xvip_cleanup_resources(&xdmsc->xvip); + return 0; +} + +static const struct of_device_id xdmsc_of_id_table[] = { + {.compatible = "xlnx,v-demosaic"}, + { } +}; +MODULE_DEVICE_TABLE(of, xdmsc_of_id_table); + +static struct platform_driver xdmsc_driver = { + .driver = { + .name = "xilinx-demosaic", + .of_match_table = xdmsc_of_id_table, + }, + .probe = xdmsc_probe, + .remove = xdmsc_remove, + +}; + +module_platform_driver(xdmsc_driver); +MODULE_DESCRIPTION("Xilinx Demosaic IP Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index c9d5fdb2d407..e8090721d25b 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -10,11 +10,13 @@ */ #include <linux/dma/xilinx_dma.h> +#include <linux/dma/xilinx_frmbuf.h> #include <linux/lcm.h> #include <linux/list.h> #include <linux/module.h> #include <linux/of.h> #include <linux/slab.h> +#include <linux/xilinx-v4l2-controls.h> #include <media/v4l2-dev.h> #include <media/v4l2-fh.h> @@ -36,6 +38,11 @@ #define XVIP_DMA_MIN_HEIGHT 1U #define XVIP_DMA_MAX_HEIGHT 8191U +struct xventity_list { + struct list_head list; + struct media_entity *entity; +}; + /* ----------------------------------------------------------------------------- * Helper functions */ @@ -60,9 +67,10 @@ static int xvip_dma_verify_format(struct xvip_dma *dma) struct v4l2_subdev_format fmt; struct v4l2_subdev *subdev; int ret; + int width, height; subdev = xvip_dma_remote_subdev(&dma->pad, &fmt.pad); - if (subdev == NULL) + if (!subdev) return -EPIPE; fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; @@ -70,10 +78,18 @@ static int xvip_dma_verify_format(struct xvip_dma *dma) if (ret < 0) return ret == -ENOIOCTLCMD ? -EINVAL : ret; - if (dma->fmtinfo->code != fmt.format.code || - dma->format.height != fmt.format.height || - dma->format.width != fmt.format.width || - dma->format.colorspace != fmt.format.colorspace) + if (dma->fmtinfo->code != fmt.format.code) + return -EINVAL; + + if (V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) { + width = dma->format.fmt.pix_mp.width; + height = dma->format.fmt.pix_mp.height; + } else { + width = dma->format.fmt.pix.width; + height = dma->format.fmt.pix.height; + } + + if (width != fmt.format.width || height != fmt.format.height) return -EINVAL; return 0; @@ -83,44 +99,135 @@ static int xvip_dma_verify_format(struct xvip_dma *dma) * Pipeline Stream Management */ +static int xvip_entity_start_stop(struct xvip_composite_device *xdev, + struct media_entity *entity, bool start) +{ + struct v4l2_subdev *subdev; + bool is_streaming; + int ret = 0; + + dev_dbg(xdev->dev, "%s entity %s\n", + start ? "Starting" : "Stopping", entity->name); + subdev = media_entity_to_v4l2_subdev(entity); + + /* This is to maintain list of stream on/off devices */ + is_streaming = xvip_subdev_set_streaming(xdev, subdev, start); + + /* + * start or stop the subdev only once in case if they are + * shared between sub-graphs + */ + if (start && !is_streaming) { + /* power-on subdevice */ + ret = v4l2_subdev_call(subdev, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_err(xdev->dev, + "s_power on failed on subdev\n"); + xvip_subdev_set_streaming(xdev, subdev, 0); + return ret; + } + + /* stream-on subdevice */ + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_err(xdev->dev, + "s_stream on failed on subdev\n"); + v4l2_subdev_call(subdev, core, s_power, 0); + xvip_subdev_set_streaming(xdev, subdev, 0); + } + } else if (!start && is_streaming) { + /* stream-off subdevice */ + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_err(xdev->dev, + "s_stream off failed on subdev\n"); + xvip_subdev_set_streaming(xdev, subdev, 1); + } + + /* power-off subdevice */ + ret = v4l2_subdev_call(subdev, core, s_power, 0); + if (ret < 0 && ret != -ENOIOCTLCMD) + dev_err(xdev->dev, + "s_power off failed on subdev\n"); + } + + return ret; +} + /** * xvip_pipeline_start_stop - Start ot stop streaming on a pipeline - * @pipe: The pipeline + * @xdev: Composite video device + * @dma: xvip dma * @start: Start (when true) or stop (when false) the pipeline * - * Walk the entities chain starting at the pipeline output video node and start - * or stop all of them. + * Walk the entities chain starting @dma and start or stop all of them * * Return: 0 if successful, or the return value of the failed video::s_stream * operation otherwise. */ -static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start) +static int xvip_pipeline_start_stop(struct xvip_composite_device *xdev, + struct xvip_dma *dma, bool start) { - struct xvip_dma *dma = pipe->output; - struct media_entity *entity; - struct media_pad *pad; - struct v4l2_subdev *subdev; - int ret; + struct media_graph graph; + struct media_entity *entity = &dma->video.entity; + struct media_device *mdev = entity->graph_obj.mdev; + struct xventity_list *temp, *_temp; + LIST_HEAD(ent_list); + int ret = 0; + + mutex_lock(&mdev->graph_mutex); + + /* Walk the graph to locate the subdev nodes */ + ret = media_graph_walk_init(&graph, mdev); + if (ret) + goto error; - entity = &dma->video.entity; - while (1) { - pad = &entity->pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - break; + media_graph_walk_start(&graph, entity); - pad = media_entity_remote_pad(pad); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - break; + /* get the list of entities */ + while ((entity = media_graph_walk_next(&graph))) { + struct xventity_list *ele; - entity = pad->entity; - subdev = media_entity_to_v4l2_subdev(entity); + /* We want to stream on/off only subdevs */ + if (!is_media_entity_v4l2_subdev(entity)) + continue; - ret = v4l2_subdev_call(subdev, video, s_stream, start); - if (start && ret < 0 && ret != -ENOIOCTLCMD) - return ret; + /* Maintain the pipeline sequence in a list */ + ele = kzalloc(sizeof(*ele), GFP_KERNEL); + if (!ele) { + ret = -ENOMEM; + goto error; + } + + ele->entity = entity; + list_add(&ele->list, &ent_list); } - return 0; + if (start) { + list_for_each_entry_safe(temp, _temp, &ent_list, list) { + /* Enable all subdevs from sink to source */ + ret = xvip_entity_start_stop(xdev, temp->entity, start); + if (ret < 0) { + dev_err(xdev->dev, "ret = %d for entity %s\n", + ret, temp->entity->name); + break; + } + } + } else { + list_for_each_entry_safe_reverse(temp, _temp, &ent_list, list) + /* Enable all subdevs from source to sink */ + xvip_entity_start_stop(xdev, temp->entity, start); + } + + list_for_each_entry_safe(temp, _temp, &ent_list, list) { + list_del(&temp->list); + kfree(temp); + } + +error: + mutex_unlock(&mdev->graph_mutex); + media_graph_walk_cleanup(&graph); + return ret; } /** @@ -133,7 +240,8 @@ static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start) * independently, pipelines have a shared stream state that enable or disable * all entities in the pipeline. For this reason the pipeline uses a streaming * counter that tracks the number of DMA engines that have requested the stream - * to be enabled. + * to be enabled. This will walk the graph starting from each DMA and enable or + * disable the entities in the path. * * When called with the @on argument set to true, this function will increment * the pipeline streaming count. If the streaming count reaches the number of @@ -150,20 +258,31 @@ static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start) */ static int xvip_pipeline_set_stream(struct xvip_pipeline *pipe, bool on) { + struct xvip_composite_device *xdev; + struct xvip_dma *dma; int ret = 0; mutex_lock(&pipe->lock); + xdev = pipe->xdev; if (on) { if (pipe->stream_count == pipe->num_dmas - 1) { - ret = xvip_pipeline_start_stop(pipe, true); - if (ret < 0) - goto done; + /* + * This will iterate the DMAs and the stream-on of + * subdevs may not be sequential due to multiple + * sub-graph path + */ + list_for_each_entry(dma, &xdev->dmas, list) { + ret = xvip_pipeline_start_stop(xdev, dma, true); + if (ret < 0) + goto done; + } } pipe->stream_count++; } else { if (--pipe->stream_count == 0) - xvip_pipeline_start_stop(pipe, false); + list_for_each_entry(dma, &xdev->dmas, list) + xvip_pipeline_start_stop(xdev, dma, false); } done: @@ -200,23 +319,22 @@ static int xvip_pipeline_validate(struct xvip_pipeline *pipe, dma = to_xvip_dma(media_entity_to_video_device(entity)); - if (dma->pad.flags & MEDIA_PAD_FL_SINK) { - pipe->output = dma; + if (dma->pad.flags & MEDIA_PAD_FL_SINK) num_outputs++; - } else { + else num_inputs++; - } } mutex_unlock(&mdev->graph_mutex); media_graph_walk_cleanup(&graph); - /* We need exactly one output and zero or one input. */ - if (num_outputs != 1 || num_inputs > 1) + /* We need at least one DMA to proceed */ + if (num_outputs == 0 && num_inputs == 0) return -EPIPE; pipe->num_dmas = num_inputs + num_outputs; + pipe->xdev = start->xdev; return 0; } @@ -224,7 +342,6 @@ static int xvip_pipeline_validate(struct xvip_pipeline *pipe, static void __xvip_pipeline_cleanup(struct xvip_pipeline *pipe) { pipe->num_dmas = 0; - pipe->output = NULL; } /** @@ -287,11 +404,13 @@ done: * @buf: vb2 buffer base object * @queue: buffer list entry in the DMA engine queued buffers list * @dma: DMA channel that uses the buffer + * @desc: Descriptor associated with this structure */ struct xvip_dma_buffer { struct vb2_v4l2_buffer buf; struct list_head queue; struct xvip_dma *dma; + struct dma_async_tx_descriptor *desc; }; #define to_xvip_dma_buffer(vb) container_of(vb, struct xvip_dma_buffer, buf) @@ -300,6 +419,9 @@ static void xvip_dma_complete(void *param) { struct xvip_dma_buffer *buf = param; struct xvip_dma *dma = buf->dma; + int i, sizeimage; + u32 fid; + int status; spin_lock(&dma->queued_lock); list_del(&buf->queue); @@ -308,7 +430,38 @@ static void xvip_dma_complete(void *param) buf->buf.field = V4L2_FIELD_NONE; buf->buf.sequence = dma->sequence++; buf->buf.vb2_buf.timestamp = ktime_get_ns(); - vb2_set_plane_payload(&buf->buf.vb2_buf, 0, dma->format.sizeimage); + + status = xilinx_xdma_get_fid(dma->dma, buf->desc, &fid); + if (!status) { + if (((V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) && + dma->format.fmt.pix_mp.field == V4L2_FIELD_ALTERNATE) || + dma->format.fmt.pix.field == V4L2_FIELD_ALTERNATE) { + /* + * fid = 1 is odd field i.e. V4L2_FIELD_TOP. + * fid = 0 is even field i.e. V4L2_FIELD_BOTTOM. + */ + buf->buf.field = fid ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; + + if (fid == dma->prev_fid) + buf->buf.sequence = dma->sequence++; + + buf->buf.sequence >>= 1; + dma->prev_fid = fid; + } + } + + if (V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) { + for (i = 0; i < dma->fmtinfo->buffers; i++) { + sizeimage = + dma->format.fmt.pix_mp.plane_fmt[i].sizeimage; + vb2_set_plane_payload(&buf->buf.vb2_buf, i, sizeimage); + } + } else { + sizeimage = dma->format.fmt.pix.sizeimage; + vb2_set_plane_payload(&buf->buf.vb2_buf, 0, sizeimage); + } + vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); } @@ -318,13 +471,39 @@ xvip_dma_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { struct xvip_dma *dma = vb2_get_drv_priv(vq); + u8 i; + int sizeimage; + + /* Multi planar case: Make sure the image size is large enough */ + if (V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) { + if (*nplanes) { + if (*nplanes != dma->format.fmt.pix_mp.num_planes) + return -EINVAL; + + for (i = 0; i < *nplanes; i++) { + sizeimage = + dma->format.fmt.pix_mp.plane_fmt[i].sizeimage; + if (sizes[i] < sizeimage) + return -EINVAL; + } + } else { + *nplanes = dma->fmtinfo->buffers; + for (i = 0; i < dma->fmtinfo->buffers; i++) { + sizeimage = + dma->format.fmt.pix_mp.plane_fmt[i].sizeimage; + sizes[i] = sizeimage; + } + } + return 0; + } - /* Make sure the image size is large enough. */ - if (*nplanes) - return sizes[0] < dma->format.sizeimage ? -EINVAL : 0; + /* Single planar case: Make sure the image size is large enough */ + sizeimage = dma->format.fmt.pix.sizeimage; + if (*nplanes == 1) + return sizes[0] < sizeimage ? -EINVAL : 0; *nplanes = 1; - sizes[0] = dma->format.sizeimage; + sizes[0] = sizeimage; return 0; } @@ -348,14 +527,19 @@ static void xvip_dma_buffer_queue(struct vb2_buffer *vb) struct dma_async_tx_descriptor *desc; dma_addr_t addr = vb2_dma_contig_plane_dma_addr(vb, 0); u32 flags; + u32 luma_size; + u32 padding_factor_nume, padding_factor_deno, bpl_nume, bpl_deno; + u32 fid = ~0; - if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE || + dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; dma->xt.dir = DMA_DEV_TO_MEM; dma->xt.src_sgl = false; dma->xt.dst_sgl = true; dma->xt.dst_start = addr; - } else { + } else if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT || + dma->queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; dma->xt.dir = DMA_MEM_TO_DEV; dma->xt.src_sgl = true; @@ -363,10 +547,66 @@ static void xvip_dma_buffer_queue(struct vb2_buffer *vb) dma->xt.src_start = addr; } - dma->xt.frame_size = 1; - dma->sgl[0].size = dma->format.width * dma->fmtinfo->bpp; - dma->sgl[0].icg = dma->format.bytesperline - dma->sgl[0].size; - dma->xt.numf = dma->format.height; + /* + * DMA IP supports only 2 planes, so one datachunk is sufficient + * to get start address of 2nd plane + */ + if (V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) { + struct v4l2_pix_format_mplane *pix_mp; + + pix_mp = &dma->format.fmt.pix_mp; + xilinx_xdma_v4l2_config(dma->dma, pix_mp->pixelformat); + xvip_width_padding_factor(pix_mp->pixelformat, + &padding_factor_nume, + &padding_factor_deno); + xvip_bpl_scaling_factor(pix_mp->pixelformat, &bpl_nume, + &bpl_deno); + dma->xt.frame_size = dma->fmtinfo->num_planes; + dma->sgl[0].size = (pix_mp->width * dma->fmtinfo->bpl_factor * + padding_factor_nume * bpl_nume) / + (padding_factor_deno * bpl_deno); + dma->sgl[0].icg = pix_mp->plane_fmt[0].bytesperline - + dma->sgl[0].size; + dma->xt.numf = pix_mp->height; + + /* + * dst_icg is the number of bytes to jump after last luma addr + * and before first chroma addr + */ + + /* Handling contiguous data with mplanes */ + if (dma->fmtinfo->buffers == 1) { + dma->sgl[0].dst_icg = 0; + } else { + /* Handling non-contiguous data with mplanes */ + if (dma->fmtinfo->buffers == 2) { + dma_addr_t chroma_addr = + vb2_dma_contig_plane_dma_addr(vb, 1); + luma_size = pix_mp->plane_fmt[0].bytesperline * + dma->xt.numf; + if (chroma_addr > addr) + dma->sgl[0].dst_icg = chroma_addr - + addr - luma_size; + } + } + } else { + struct v4l2_pix_format *pix; + + pix = &dma->format.fmt.pix; + xilinx_xdma_v4l2_config(dma->dma, pix->pixelformat); + xvip_width_padding_factor(pix->pixelformat, + &padding_factor_nume, + &padding_factor_deno); + xvip_bpl_scaling_factor(pix->pixelformat, &bpl_nume, + &bpl_deno); + dma->xt.frame_size = dma->fmtinfo->num_planes; + dma->sgl[0].size = (pix->width * dma->fmtinfo->bpl_factor * + padding_factor_nume * bpl_nume) / + (padding_factor_deno * bpl_deno); + dma->sgl[0].icg = pix->bytesperline - dma->sgl[0].size; + dma->xt.numf = pix->height; + dma->sgl[0].dst_icg = 0; + } desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags); if (!desc) { @@ -376,6 +616,28 @@ static void xvip_dma_buffer_queue(struct vb2_buffer *vb) } desc->callback = xvip_dma_complete; desc->callback_param = buf; + buf->desc = desc; + + if (buf->buf.field == V4L2_FIELD_TOP) + fid = 1; + else if (buf->buf.field == V4L2_FIELD_BOTTOM) + fid = 0; + else if (buf->buf.field == V4L2_FIELD_NONE) + fid = 0; + + xilinx_xdma_set_fid(dma->dma, desc, fid); + + /* Set low latency capture mode */ + if (dma->earlycb_mode) { + int ret; + + ret = xilinx_xdma_set_earlycb(dma->dma, desc, + dma->earlycb_mode); + if (ret < 0) { + dev_err(dma->xdev->dev, + "Failed enable low latency mode\n"); + } + } spin_lock_irq(&dma->queued_lock); list_add_tail(&buf->queue, &dma->queued_bufs); @@ -395,6 +657,7 @@ static int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count) int ret; dma->sequence = 0; + dma->prev_fid = ~0; /* * Start streaming on the pipeline. No link touching an entity in the @@ -403,10 +666,12 @@ static int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count) * Use the pipeline object embedded in the first DMA object that starts * streaming. */ + mutex_lock(&dma->xdev->lock); pipe = dma->video.entity.pipe ? to_xvip_pipeline(&dma->video.entity) : &dma->pipe; ret = media_pipeline_start(&dma->video.entity, &pipe->pipe); + mutex_unlock(&dma->xdev->lock); if (ret < 0) goto error; @@ -427,7 +692,9 @@ static int xvip_dma_start_streaming(struct vb2_queue *vq, unsigned int count) dma_async_issue_pending(dma->dma); /* Start the pipeline. */ - xvip_pipeline_set_stream(pipe, true); + ret = xvip_pipeline_set_stream(pipe, true); + if (ret < 0) + goto error_stop; return 0; @@ -435,6 +702,7 @@ error_stop: media_pipeline_stop(&dma->video.entity); error: + dmaengine_terminate_all(dma->dma); /* Give back all queued buffers to videobuf2. */ spin_lock_irq(&dma->queued_lock); list_for_each_entry_safe(buf, nbuf, &dma->queued_bufs, queue) { @@ -493,10 +761,20 @@ xvip_dma_querycap(struct file *file, void *fh, struct v4l2_capability *cap) cap->device_caps = V4L2_CAP_STREAMING; - if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + switch (dma->queue.type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE_MPLANE; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; - else + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + cap->device_caps |= V4L2_CAP_VIDEO_OUTPUT_MPLANE; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: cap->device_caps |= V4L2_CAP_VIDEO_OUTPUT; + break; + } cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | dma->xdev->v4l2_caps; @@ -509,6 +787,61 @@ xvip_dma_querycap(struct file *file, void *fh, struct v4l2_capability *cap) return 0; } +static int xvip_xdma_enum_fmt(struct xvip_dma *dma, struct v4l2_fmtdesc *f, + struct v4l2_subdev_format *v4l_fmt) +{ + const struct xvip_video_format *fmt; + int ret; + u32 i, fmt_cnt, *fmts; + + ret = xilinx_xdma_get_v4l2_vid_fmts(dma->dma, &fmt_cnt, &fmts); + if (ret) + return ret; + + /* Has media pad value changed? */ + if (v4l_fmt->format.code != dma->remote_subdev_med_bus || + !dma->remote_subdev_med_bus) { + /* Re-generate legal list of fourcc codes */ + dma->poss_v4l2_fmt_cnt = 0; + dma->remote_subdev_med_bus = v4l_fmt->format.code; + + if (!dma->poss_v4l2_fmts) { + dma->poss_v4l2_fmts = + devm_kzalloc(&dma->video.dev, + sizeof(u32) * fmt_cnt, + GFP_KERNEL); + if (!dma->poss_v4l2_fmts) + return -ENOMEM; + } + + for (i = 0; i < fmt_cnt; i++) { + fmt = xvip_get_format_by_fourcc(fmts[i]); + if (IS_ERR(fmt)) + return PTR_ERR(fmt); + + if (fmt->code != dma->remote_subdev_med_bus) + continue; + + dma->poss_v4l2_fmts[dma->poss_v4l2_fmt_cnt++] = fmts[i]; + } + } + + /* Return err if index is greater than count of legal values */ + if (f->index >= dma->poss_v4l2_fmt_cnt) + return -EINVAL; + + /* Else return pix format in table */ + fmt = xvip_get_format_by_fourcc(dma->poss_v4l2_fmts[f->index]); + if (IS_ERR(fmt)) + return PTR_ERR(fmt); + + f->pixelformat = fmt->fourcc; + strlcpy(f->description, fmt->description, + sizeof(f->description)); + + return 0; +} + /* FIXME: without this callback function, some applications are not configured * with correct formats, and it results in frames in wrong format. Whether this * callback needs to be required is not clearly defined, so it should be @@ -519,12 +852,49 @@ xvip_dma_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) { struct v4l2_fh *vfh = file->private_data; struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + struct v4l2_subdev *subdev; + struct v4l2_subdev_format v4l_fmt; + const struct xvip_video_format *fmt; + int err, ret; + + /* Establish media pad format */ + subdev = xvip_dma_remote_subdev(&dma->pad, &v4l_fmt.pad); + if (!subdev) + return -EPIPE; + + v4l_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &v4l_fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + /* + * In case of frmbuf DMA, this will invoke frambuf driver specific APIs + * to enumerate formats otherwise return the pix format corresponding + * to subdev's media bus format. This kind of separation would be + * helpful for clean up and upstreaming. + */ + err = xvip_xdma_enum_fmt(dma, f, &v4l_fmt); + if (!err) + return err; + /* + * This logic will just return one pix format based on subdev's + * media bus format + */ if (f->index > 0) return -EINVAL; - f->pixelformat = dma->format.pixelformat; - strscpy(f->description, dma->fmtinfo->description, + if (V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) + f->pixelformat = dma->format.fmt.pix_mp.pixelformat; + else + f->pixelformat = dma->format.fmt.pix.pixelformat; + + fmt = xvip_get_format_by_code(v4l_fmt.format.code); + if (IS_ERR(fmt)) + return PTR_ERR(fmt); + + f->pixelformat = fmt->fourcc; + strlcpy(f->description, fmt->description, sizeof(f->description)); return 0; @@ -536,13 +906,17 @@ xvip_dma_get_format(struct file *file, void *fh, struct v4l2_format *format) struct v4l2_fh *vfh = file->private_data; struct xvip_dma *dma = to_xvip_dma(vfh->vdev); - format->fmt.pix = dma->format; + if (V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) + format->fmt.pix_mp = dma->format.fmt.pix_mp; + else + format->fmt.pix = dma->format.fmt.pix; return 0; } static void -__xvip_dma_try_format(struct xvip_dma *dma, struct v4l2_pix_format *pix, +__xvip_dma_try_format(struct xvip_dma *dma, + struct v4l2_format *format, const struct xvip_video_format **fmtinfo) { const struct xvip_video_format *info; @@ -553,40 +927,144 @@ __xvip_dma_try_format(struct xvip_dma *dma, struct v4l2_pix_format *pix, unsigned int width; unsigned int align; unsigned int bpl; + unsigned int i, hsub, vsub, plane_width, plane_height; + unsigned int fourcc; + unsigned int padding_factor_nume, padding_factor_deno; + unsigned int bpl_nume, bpl_deno; + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + int ret; + + subdev = xvip_dma_remote_subdev(&dma->pad, &fmt.pad); + if (!subdev) + return; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return; + + if (fmt.format.field == V4L2_FIELD_ALTERNATE) { + if (V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) + dma->format.fmt.pix_mp.field = V4L2_FIELD_ALTERNATE; + else + dma->format.fmt.pix.field = V4L2_FIELD_ALTERNATE; + } else { + if (V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) + dma->format.fmt.pix_mp.field = V4L2_FIELD_NONE; + else + dma->format.fmt.pix.field = V4L2_FIELD_NONE; + } /* Retrieve format information and select the default format if the * requested format isn't supported. */ - info = xvip_get_format_by_fourcc(pix->pixelformat); + if (V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) + fourcc = format->fmt.pix_mp.pixelformat; + else + fourcc = format->fmt.pix.pixelformat; + + info = xvip_get_format_by_fourcc(fourcc); + if (IS_ERR(info)) info = xvip_get_format_by_fourcc(XVIP_DMA_DEF_FORMAT); - pix->pixelformat = info->fourcc; - pix->field = V4L2_FIELD_NONE; + xvip_width_padding_factor(info->fourcc, &padding_factor_nume, + &padding_factor_deno); + xvip_bpl_scaling_factor(info->fourcc, &bpl_nume, &bpl_deno); /* The transfer alignment requirements are expressed in bytes. Compute * the minimum and maximum values, clamp the requested width and convert * it back to pixels. */ - align = lcm(dma->align, info->bpp); + align = lcm(dma->align, info->bpp >> 3); min_width = roundup(XVIP_DMA_MIN_WIDTH, align); max_width = rounddown(XVIP_DMA_MAX_WIDTH, align); - width = rounddown(pix->width * info->bpp, align); - - pix->width = clamp(width, min_width, max_width) / info->bpp; - pix->height = clamp(pix->height, XVIP_DMA_MIN_HEIGHT, - XVIP_DMA_MAX_HEIGHT); - /* Clamp the requested bytes per line value. If the maximum bytes per - * line value is zero, the module doesn't support user configurable line - * sizes. Override the requested value with the minimum in that case. - */ - min_bpl = pix->width * info->bpp; - max_bpl = rounddown(XVIP_DMA_MAX_WIDTH, dma->align); - bpl = rounddown(pix->bytesperline, dma->align); - - pix->bytesperline = clamp(bpl, min_bpl, max_bpl); - pix->sizeimage = pix->bytesperline * pix->height; + if (V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) { + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_plane_pix_format *plane_fmt; + + pix_mp = &format->fmt.pix_mp; + plane_fmt = pix_mp->plane_fmt; + pix_mp->field = dma->format.fmt.pix_mp.field; + width = rounddown(pix_mp->width * info->bpl_factor, align); + pix_mp->width = clamp(width, min_width, max_width) / + info->bpl_factor; + pix_mp->height = clamp(pix_mp->height, XVIP_DMA_MIN_HEIGHT, + XVIP_DMA_MAX_HEIGHT); + + /* + * Clamp the requested bytes per line value. If the maximum + * bytes per line value is zero, the module doesn't support + * user configurable line sizes. Override the requested value + * with the minimum in that case. + */ + + max_bpl = rounddown(XVIP_DMA_MAX_WIDTH, dma->align); + + /* Handling contiguous data with mplanes */ + if (info->buffers == 1) { + min_bpl = (pix_mp->width * info->bpl_factor * + padding_factor_nume * bpl_nume) / + (padding_factor_deno * bpl_deno); + min_bpl = roundup(min_bpl, dma->align); + bpl = roundup(plane_fmt[0].bytesperline, dma->align); + plane_fmt[0].bytesperline = clamp(bpl, min_bpl, + max_bpl); + + if (info->num_planes == 1) { + /* Single plane formats */ + plane_fmt[0].sizeimage = + plane_fmt[0].bytesperline * + pix_mp->height; + } else { + /* Multi plane formats */ + plane_fmt[0].sizeimage = + DIV_ROUND_UP(plane_fmt[0].bytesperline * + pix_mp->height * + info->bpp, 8); + } + } else { + /* Handling non-contiguous data with mplanes */ + hsub = info->hsub; + vsub = info->vsub; + for (i = 0; i < info->num_planes; i++) { + plane_width = pix_mp->width / (i ? hsub : 1); + plane_height = pix_mp->height / (i ? vsub : 1); + min_bpl = (plane_width * info->bpl_factor * + padding_factor_nume * bpl_nume) / + (padding_factor_deno * bpl_deno); + min_bpl = roundup(min_bpl, dma->align); + bpl = rounddown(plane_fmt[i].bytesperline, + dma->align); + plane_fmt[i].bytesperline = + clamp(bpl, min_bpl, max_bpl); + plane_fmt[i].sizeimage = + plane_fmt[i].bytesperline * + plane_height; + } + } + } else { + struct v4l2_pix_format *pix; + + pix = &format->fmt.pix; + pix->field = dma->format.fmt.pix.field; + width = rounddown(pix->width * info->bpl_factor, align); + pix->width = clamp(width, min_width, max_width) / + info->bpl_factor; + pix->height = clamp(pix->height, XVIP_DMA_MIN_HEIGHT, + XVIP_DMA_MAX_HEIGHT); + + min_bpl = (pix->width * info->bpl_factor * + padding_factor_nume * bpl_nume) / + (padding_factor_deno * bpl_deno); + min_bpl = roundup(min_bpl, dma->align); + max_bpl = rounddown(XVIP_DMA_MAX_WIDTH, dma->align); + bpl = rounddown(pix->bytesperline, dma->align); + pix->bytesperline = clamp(bpl, min_bpl, max_bpl); + pix->sizeimage = pix->width * pix->height * info->bpp / 8; + } if (fmtinfo) *fmtinfo = info; @@ -598,7 +1076,7 @@ xvip_dma_try_format(struct file *file, void *fh, struct v4l2_format *format) struct v4l2_fh *vfh = file->private_data; struct xvip_dma *dma = to_xvip_dma(vfh->vdev); - __xvip_dma_try_format(dma, &format->fmt.pix, NULL); + __xvip_dma_try_format(dma, format, NULL); return 0; } @@ -609,26 +1087,59 @@ xvip_dma_set_format(struct file *file, void *fh, struct v4l2_format *format) struct xvip_dma *dma = to_xvip_dma(vfh->vdev); const struct xvip_video_format *info; - __xvip_dma_try_format(dma, &format->fmt.pix, &info); + __xvip_dma_try_format(dma, format, &info); if (vb2_is_busy(&dma->queue)) return -EBUSY; - dma->format = format->fmt.pix; + if (V4L2_TYPE_IS_MULTIPLANAR(dma->format.type)) + dma->format.fmt.pix_mp = format->fmt.pix_mp; + else + dma->format.fmt.pix = format->fmt.pix; + dma->fmtinfo = info; return 0; } +static int +xvip_dma_set_ctrl(struct file *file, void *fh, struct v4l2_control *ctl) +{ + struct v4l2_fh *vfh = file->private_data; + struct xvip_dma *dma = to_xvip_dma(vfh->vdev); + + if (vb2_is_busy(&dma->queue)) + return -EBUSY; + + if (ctl->id == V4L2_CID_XILINX_LOW_LATENCY) { + if (ctl->value) + dma->earlycb_mode = EARLY_CALLBACK_LOW_LATENCY; + else + dma->earlycb_mode = 0; + } + + return 0; +} + static const struct v4l2_ioctl_ops xvip_dma_ioctl_ops = { .vidioc_querycap = xvip_dma_querycap, .vidioc_enum_fmt_vid_cap = xvip_dma_enum_format, + .vidioc_enum_fmt_vid_cap_mplane = xvip_dma_enum_format, + .vidioc_enum_fmt_vid_out = xvip_dma_enum_format, + .vidioc_enum_fmt_vid_out_mplane = xvip_dma_enum_format, .vidioc_g_fmt_vid_cap = xvip_dma_get_format, + .vidioc_g_fmt_vid_cap_mplane = xvip_dma_get_format, .vidioc_g_fmt_vid_out = xvip_dma_get_format, + .vidioc_g_fmt_vid_out_mplane = xvip_dma_get_format, .vidioc_s_fmt_vid_cap = xvip_dma_set_format, + .vidioc_s_fmt_vid_cap_mplane = xvip_dma_set_format, .vidioc_s_fmt_vid_out = xvip_dma_set_format, + .vidioc_s_fmt_vid_out_mplane = xvip_dma_set_format, + .vidioc_s_ctrl = xvip_dma_set_ctrl, .vidioc_try_fmt_vid_cap = xvip_dma_try_format, + .vidioc_try_fmt_vid_cap_mplane = xvip_dma_try_format, .vidioc_try_fmt_vid_out = xvip_dma_try_format, + .vidioc_try_fmt_vid_out_mplane = xvip_dma_try_format, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_querybuf = vb2_ioctl_querybuf, .vidioc_qbuf = vb2_ioctl_qbuf, @@ -661,6 +1172,7 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, { char name[16]; int ret; + u32 i, hsub, vsub, width, height; dma->xdev = xdev; dma->port = port; @@ -670,17 +1182,56 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, spin_lock_init(&dma->queued_lock); dma->fmtinfo = xvip_get_format_by_fourcc(XVIP_DMA_DEF_FORMAT); - dma->format.pixelformat = dma->fmtinfo->fourcc; - dma->format.colorspace = V4L2_COLORSPACE_SRGB; - dma->format.field = V4L2_FIELD_NONE; - dma->format.width = XVIP_DMA_DEF_WIDTH; - dma->format.height = XVIP_DMA_DEF_HEIGHT; - dma->format.bytesperline = dma->format.width * dma->fmtinfo->bpp; - dma->format.sizeimage = dma->format.bytesperline * dma->format.height; + dma->format.type = type; + + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + struct v4l2_pix_format_mplane *pix_mp; + + pix_mp = &dma->format.fmt.pix_mp; + pix_mp->pixelformat = dma->fmtinfo->fourcc; + pix_mp->colorspace = V4L2_COLORSPACE_SRGB; + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->width = XVIP_DMA_DEF_WIDTH; + + /* Handling contiguous data with mplanes */ + if (dma->fmtinfo->buffers == 1) { + pix_mp->plane_fmt[0].bytesperline = + pix_mp->width * dma->fmtinfo->bpl_factor; + pix_mp->plane_fmt[0].sizeimage = + pix_mp->width * pix_mp->height * + dma->fmtinfo->bpp / 8; + } else { + /* Handling non-contiguous data with mplanes */ + hsub = dma->fmtinfo->hsub; + vsub = dma->fmtinfo->vsub; + for (i = 0; i < dma->fmtinfo->buffers; i++) { + width = pix_mp->width / (i ? hsub : 1); + height = pix_mp->height / (i ? vsub : 1); + pix_mp->plane_fmt[i].bytesperline = + width * dma->fmtinfo->bpl_factor; + pix_mp->plane_fmt[i].sizeimage = width * height; + } + } + } else { + struct v4l2_pix_format *pix; + + pix = &dma->format.fmt.pix; + pix->pixelformat = dma->fmtinfo->fourcc; + pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->field = V4L2_FIELD_NONE; + pix->width = XVIP_DMA_DEF_WIDTH; + pix->height = XVIP_DMA_DEF_HEIGHT; + pix->bytesperline = pix->width * dma->fmtinfo->bpl_factor; + pix->sizeimage = + pix->width * pix->height * dma->fmtinfo->bpp / 8; + } /* Initialize the media entity... */ - dma->pad.flags = type == V4L2_BUF_TYPE_VIDEO_CAPTURE - ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE || + type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + dma->pad.flags = MEDIA_PAD_FL_SINK; + else + dma->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&dma->video.entity, 1, &dma->pad); if (ret < 0) @@ -692,11 +1243,18 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, dma->video.queue = &dma->queue; snprintf(dma->video.name, sizeof(dma->video.name), "%pOFn %s %u", xdev->dev->of_node, - type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "output" : "input", + (type == V4L2_BUF_TYPE_VIDEO_CAPTURE || + type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + ? "output" : "input", port); + dma->video.vfl_type = VFL_TYPE_GRABBER; - dma->video.vfl_dir = type == V4L2_BUF_TYPE_VIDEO_CAPTURE - ? VFL_DIR_RX : VFL_DIR_TX; + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE || + type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + dma->video.vfl_dir = VFL_DIR_RX; + else + dma->video.vfl_dir = VFL_DIR_TX; + dma->video.release = video_device_release_empty; dma->video.ioctl_ops = &xvip_dma_ioctl_ops; dma->video.lock = &dma->lock; @@ -729,10 +1287,12 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, /* ... and the DMA channel. */ snprintf(name, sizeof(name), "port%u", port); - dma->dma = dma_request_slave_channel(dma->xdev->dev, name); - if (dma->dma == NULL) { - dev_err(dma->xdev->dev, "no VDMA channel found\n"); - ret = -ENODEV; + dma->dma = dma_request_chan(dma->xdev->dev, name); + if (IS_ERR(dma->dma)) { + ret = PTR_ERR(dma->dma); + if (ret != -EPROBE_DEFER) + dev_err(dma->xdev->dev, + "No Video DMA channel found"); goto error; } @@ -756,7 +1316,7 @@ void xvip_dma_cleanup(struct xvip_dma *dma) if (video_is_registered(&dma->video)) video_unregister_device(&dma->video); - if (dma->dma) + if (!IS_ERR(dma->dma)) dma_release_channel(dma->dma); media_entity_cleanup(&dma->video.entity); diff --git a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h index 5aec4d17eb21..61c26ab103f7 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.h +++ b/drivers/media/platform/xilinx/xilinx-dma.h @@ -32,7 +32,7 @@ struct xvip_video_format; * @use_count: number of DMA engines using the pipeline * @stream_count: number of DMA engines currently streaming * @num_dmas: number of DMA engines in the pipeline - * @output: DMA engine at the output of the pipeline + * @xdev: Composite device the pipe belongs to */ struct xvip_pipeline { struct media_pipeline pipe; @@ -42,7 +42,7 @@ struct xvip_pipeline { unsigned int stream_count; unsigned int num_dmas; - struct xvip_dma *output; + struct xvip_composite_device *xdev; }; static inline struct xvip_pipeline *to_xvip_pipeline(struct media_entity *e) @@ -55,12 +55,15 @@ static inline struct xvip_pipeline *to_xvip_pipeline(struct media_entity *e) * @list: list entry in a composite device dmas list * @video: V4L2 video device associated with the DMA channel * @pad: media pad for the video device entity + * @remote_subdev_med_bus: media bus format of sub-device * @xdev: composite device the DMA channel belongs to * @pipe: pipeline belonging to the DMA channel * @port: composite device DT node port number for the DMA channel * @lock: protects the @format, @fmtinfo and @queue fields * @format: active V4L2 pixel format * @fmtinfo: format information corresponding to the active @format + * @poss_v4l2_fmts: All possible v4l formats supported + * @poss_v4l2_fmt_cnt: number of supported v4l formats * @queue: vb2 buffers queue * @sequence: V4L2 buffers sequence number * @queued_bufs: list of queued buffers @@ -69,19 +72,23 @@ static inline struct xvip_pipeline *to_xvip_pipeline(struct media_entity *e) * @align: transfer alignment required by the DMA channel (in bytes) * @xt: dma interleaved template for dma configuration * @sgl: data chunk structure for dma_interleaved_template + * @prev_fid: Previous Field ID */ struct xvip_dma { struct list_head list; struct video_device video; struct media_pad pad; + u32 remote_subdev_med_bus; struct xvip_composite_device *xdev; struct xvip_pipeline pipe; unsigned int port; struct mutex lock; - struct v4l2_pix_format format; + struct v4l2_format format; const struct xvip_video_format *fmtinfo; + u32 *poss_v4l2_fmts; + u32 poss_v4l2_fmt_cnt; struct vb2_queue queue; unsigned int sequence; @@ -93,6 +100,9 @@ struct xvip_dma { unsigned int align; struct dma_interleaved_template xt; struct data_chunk sgl[1]; + + u32 prev_fid; + u32 earlycb_mode; }; #define to_xvip_dma(vdev) container_of(vdev, struct xvip_dma, video) diff --git a/drivers/media/platform/xilinx/xilinx-gamma-coeff.h b/drivers/media/platform/xilinx/xilinx-gamma-coeff.h new file mode 100644 index 000000000000..344260008a47 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-gamma-coeff.h @@ -0,0 +1,5385 @@ +/* + * Xilinx Gamma Correction IP + * + * Copyright (C) 2017 Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __XILINX_GAMMA_COEFF_H__ +#define __XILINX_GAMMA_COEFF_H__ + +#define GAMMA_CURVE_LENGTH (40) + +#define GAMMA_BPC_8 (8) +#define GAMMA8_TABLE_LENGTH BIT(GAMMA_BPC_8) +static const u16 xgamma8_01[GAMMA8_TABLE_LENGTH] = { + 0, 147, 157, 164, 168, 172, 175, 178, + 180, 183, 184, 186, 188, 189, 191, 192, + 193, 195, 196, 197, 198, 199, 200, 200, + 201, 202, 203, 204, 204, 205, 206, 207, + 207, 208, 208, 209, 210, 210, 211, 211, + 212, 212, 213, 213, 214, 214, 215, 215, + 216, 216, 217, 217, 218, 218, 218, 219, + 219, 220, 220, 220, 221, 221, 221, 222, + 222, 222, 223, 223, 223, 224, 224, 224, + 225, 225, 225, 226, 226, 226, 227, 227, + 227, 227, 228, 228, 228, 228, 229, 229, + 229, 230, 230, 230, 230, 231, 231, 231, + 231, 232, 232, 232, 232, 232, 233, 233, + 233, 233, 234, 234, 234, 234, 234, 235, + 235, 235, 235, 235, 236, 236, 236, 236, + 236, 237, 237, 237, 237, 237, 238, 238, + 238, 238, 238, 239, 239, 239, 239, 239, + 239, 240, 240, 240, 240, 240, 240, 241, + 241, 241, 241, 241, 241, 242, 242, 242, + 242, 242, 242, 243, 243, 243, 243, 243, + 243, 244, 244, 244, 244, 244, 244, 244, + 245, 245, 245, 245, 245, 245, 245, 246, + 246, 246, 246, 246, 246, 246, 247, 247, + 247, 247, 247, 247, 247, 247, 248, 248, + 248, 248, 248, 248, 248, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 250, 250, + 250, 250, 250, 250, 250, 250, 251, 251, + 251, 251, 251, 251, 251, 251, 251, 252, + 252, 252, 252, 252, 252, 252, 252, 252, + 253, 253, 253, 253, 253, 253, 253, 253, + 253, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 254, 255, 255, 255, 255, 255, +}; + +static const u16 xgamma8_02[GAMMA8_TABLE_LENGTH] = { + 0, 84, 97, 105, 111, 116, 120, 124, + 128, 131, 133, 136, 138, 141, 143, 145, + 147, 148, 150, 152, 153, 155, 156, 158, + 159, 160, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 179, 180, 181, 182, + 183, 183, 184, 185, 186, 186, 187, 188, + 188, 189, 190, 190, 191, 192, 192, 193, + 193, 194, 195, 195, 196, 196, 197, 197, + 198, 199, 199, 200, 200, 201, 201, 202, + 202, 203, 203, 204, 204, 205, 205, 206, + 206, 207, 207, 208, 208, 208, 209, 209, + 210, 210, 211, 211, 211, 212, 212, 213, + 213, 214, 214, 214, 215, 215, 216, 216, + 216, 217, 217, 217, 218, 218, 219, 219, + 219, 220, 220, 220, 221, 221, 221, 222, + 222, 223, 223, 223, 224, 224, 224, 225, + 225, 225, 226, 226, 226, 227, 227, 227, + 227, 228, 228, 228, 229, 229, 229, 230, + 230, 230, 231, 231, 231, 231, 232, 232, + 232, 233, 233, 233, 233, 234, 234, 234, + 235, 235, 235, 235, 236, 236, 236, 237, + 237, 237, 237, 238, 238, 238, 238, 239, + 239, 239, 239, 240, 240, 240, 240, 241, + 241, 241, 241, 242, 242, 242, 242, 243, + 243, 243, 243, 244, 244, 244, 244, 245, + 245, 245, 245, 246, 246, 246, 246, 246, + 247, 247, 247, 247, 248, 248, 248, 248, + 248, 249, 249, 249, 249, 250, 250, 250, + 250, 250, 251, 251, 251, 251, 252, 252, + 252, 252, 252, 253, 253, 253, 253, 253, + 254, 254, 254, 254, 254, 255, 255, 255, +}; + +static const u16 xgamma8_03[GAMMA8_TABLE_LENGTH] = { + 0, 48, 60, 67, 73, 78, 83, 87, + 90, 94, 97, 99, 102, 104, 107, 109, + 111, 113, 115, 117, 119, 121, 122, 124, + 125, 127, 129, 130, 131, 133, 134, 136, + 137, 138, 139, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 151, 152, 153, 154, + 155, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 164, 165, 166, 167, 168, + 168, 169, 170, 171, 172, 172, 173, 174, + 174, 175, 176, 177, 177, 178, 179, 179, + 180, 181, 181, 182, 183, 183, 184, 185, + 185, 186, 187, 187, 188, 188, 189, 190, + 190, 191, 191, 192, 193, 193, 194, 194, + 195, 195, 196, 197, 197, 198, 198, 199, + 199, 200, 200, 201, 201, 202, 202, 203, + 203, 204, 204, 205, 205, 206, 206, 207, + 207, 208, 208, 209, 209, 210, 210, 211, + 211, 212, 212, 213, 213, 213, 214, 214, + 215, 215, 216, 216, 217, 217, 217, 218, + 218, 219, 219, 220, 220, 220, 221, 221, + 222, 222, 223, 223, 223, 224, 224, 225, + 225, 225, 226, 226, 227, 227, 227, 228, + 228, 229, 229, 229, 230, 230, 230, 231, + 231, 232, 232, 232, 233, 233, 233, 234, + 234, 235, 235, 235, 236, 236, 236, 237, + 237, 237, 238, 238, 238, 239, 239, 240, + 240, 240, 241, 241, 241, 242, 242, 242, + 243, 243, 243, 244, 244, 244, 245, 245, + 245, 246, 246, 246, 247, 247, 247, 248, + 248, 248, 249, 249, 249, 249, 250, 250, + 250, 251, 251, 251, 252, 252, 252, 253, + 253, 253, 253, 254, 254, 254, 255, 255, +}; + +static const u16 xgamma8_04[GAMMA8_TABLE_LENGTH] = { + 0, 28, 37, 43, 48, 53, 57, 61, + 64, 67, 70, 73, 75, 78, 80, 82, + 84, 86, 88, 90, 92, 94, 96, 97, + 99, 101, 102, 104, 105, 107, 108, 110, + 111, 113, 114, 115, 117, 118, 119, 120, + 122, 123, 124, 125, 126, 127, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 149, 150, 151, 152, 153, + 154, 155, 155, 156, 157, 158, 159, 160, + 160, 161, 162, 163, 164, 164, 165, 166, + 167, 167, 168, 169, 170, 170, 171, 172, + 173, 173, 174, 175, 175, 176, 177, 177, + 178, 179, 179, 180, 181, 182, 182, 183, + 183, 184, 185, 185, 186, 187, 187, 188, + 189, 189, 190, 190, 191, 192, 192, 193, + 194, 194, 195, 195, 196, 197, 197, 198, + 198, 199, 199, 200, 201, 201, 202, 202, + 203, 203, 204, 205, 205, 206, 206, 207, + 207, 208, 208, 209, 209, 210, 211, 211, + 212, 212, 213, 213, 214, 214, 215, 215, + 216, 216, 217, 217, 218, 218, 219, 219, + 220, 220, 221, 221, 222, 222, 223, 223, + 224, 224, 225, 225, 226, 226, 227, 227, + 228, 228, 229, 229, 230, 230, 230, 231, + 231, 232, 232, 233, 233, 234, 234, 235, + 235, 235, 236, 236, 237, 237, 238, 238, + 239, 239, 240, 240, 240, 241, 241, 242, + 242, 243, 243, 243, 244, 244, 245, 245, + 246, 246, 246, 247, 247, 248, 248, 248, + 249, 249, 250, 250, 251, 251, 251, 252, + 252, 253, 253, 253, 254, 254, 255, 255, +}; + +static const u16 xgamma8_05[GAMMA8_TABLE_LENGTH] = { + 0, 16, 23, 28, 32, 36, 39, 42, + 45, 48, 50, 53, 55, 58, 60, 62, + 64, 66, 68, 70, 71, 73, 75, 77, + 78, 80, 81, 83, 84, 86, 87, 89, + 90, 92, 93, 94, 96, 97, 98, 100, + 101, 102, 103, 105, 106, 107, 108, 109, + 111, 112, 113, 114, 115, 116, 117, 118, + 119, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 145, 146, 147, 148, 149, + 150, 151, 151, 152, 153, 154, 155, 156, + 156, 157, 158, 159, 160, 160, 161, 162, + 163, 164, 164, 165, 166, 167, 167, 168, + 169, 170, 170, 171, 172, 173, 173, 174, + 175, 176, 176, 177, 178, 179, 179, 180, + 181, 181, 182, 183, 183, 184, 185, 186, + 186, 187, 188, 188, 189, 190, 190, 191, + 192, 192, 193, 194, 194, 195, 196, 196, + 197, 198, 198, 199, 199, 200, 201, 201, + 202, 203, 203, 204, 204, 205, 206, 206, + 207, 208, 208, 209, 209, 210, 211, 211, + 212, 212, 213, 214, 214, 215, 215, 216, + 217, 217, 218, 218, 219, 220, 220, 221, + 221, 222, 222, 223, 224, 224, 225, 225, + 226, 226, 227, 228, 228, 229, 229, 230, + 230, 231, 231, 232, 233, 233, 234, 234, + 235, 235, 236, 236, 237, 237, 238, 238, + 239, 240, 240, 241, 241, 242, 242, 243, + 243, 244, 244, 245, 245, 246, 246, 247, + 247, 248, 248, 249, 249, 250, 250, 251, + 251, 252, 252, 253, 253, 254, 254, 255, +}; + +static const u16 xgamma8_06[GAMMA8_TABLE_LENGTH] = { + 0, 9, 14, 18, 21, 24, 27, 29, + 32, 34, 37, 39, 41, 43, 45, 47, + 48, 50, 52, 54, 55, 57, 59, 60, + 62, 63, 65, 66, 68, 69, 71, 72, + 73, 75, 76, 77, 79, 80, 81, 83, + 84, 85, 86, 88, 89, 90, 91, 92, + 94, 95, 96, 97, 98, 99, 100, 102, + 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 145, 146, 147, 148, + 149, 150, 151, 151, 152, 153, 154, 155, + 156, 156, 157, 158, 159, 160, 161, 161, + 162, 163, 164, 165, 165, 166, 167, 168, + 169, 169, 170, 171, 172, 173, 173, 174, + 175, 176, 176, 177, 178, 179, 179, 180, + 181, 182, 182, 183, 184, 185, 185, 186, + 187, 188, 188, 189, 190, 191, 191, 192, + 193, 194, 194, 195, 196, 196, 197, 198, + 199, 199, 200, 201, 201, 202, 203, 203, + 204, 205, 206, 206, 207, 208, 208, 209, + 210, 210, 211, 212, 212, 213, 214, 214, + 215, 216, 216, 217, 218, 218, 219, 220, + 220, 221, 222, 222, 223, 224, 224, 225, + 226, 226, 227, 228, 228, 229, 230, 230, + 231, 231, 232, 233, 233, 234, 235, 235, + 236, 237, 237, 238, 238, 239, 240, 240, + 241, 242, 242, 243, 243, 244, 245, 245, + 246, 247, 247, 248, 248, 249, 250, 250, + 251, 251, 252, 253, 253, 254, 254, 255, +}; + +static const u16 xgamma8_07[GAMMA8_TABLE_LENGTH] = { + 0, 5, 9, 11, 14, 16, 18, 21, + 23, 25, 26, 28, 30, 32, 33, 35, + 37, 38, 40, 41, 43, 44, 46, 47, + 49, 50, 52, 53, 54, 56, 57, 58, + 60, 61, 62, 64, 65, 66, 67, 69, + 70, 71, 72, 73, 75, 76, 77, 78, + 79, 80, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 142, + 143, 144, 145, 146, 147, 148, 149, 150, + 150, 151, 152, 153, 154, 155, 156, 157, + 157, 158, 159, 160, 161, 162, 163, 163, + 164, 165, 166, 167, 168, 168, 169, 170, + 171, 172, 173, 173, 174, 175, 176, 177, + 178, 178, 179, 180, 181, 182, 182, 183, + 184, 185, 186, 186, 187, 188, 189, 190, + 190, 191, 192, 193, 194, 194, 195, 196, + 197, 197, 198, 199, 200, 201, 201, 202, + 203, 204, 204, 205, 206, 207, 208, 208, + 209, 210, 211, 211, 212, 213, 214, 214, + 215, 216, 217, 217, 218, 219, 220, 220, + 221, 222, 223, 223, 224, 225, 226, 226, + 227, 228, 228, 229, 230, 231, 231, 232, + 233, 234, 234, 235, 236, 237, 237, 238, + 239, 239, 240, 241, 242, 242, 243, 244, + 244, 245, 246, 247, 247, 248, 249, 249, + 250, 251, 251, 252, 253, 254, 254, 255, +}; + +static const u16 xgamma8_08[GAMMA8_TABLE_LENGTH] = { + 0, 3, 5, 7, 9, 11, 13, 14, + 16, 18, 19, 21, 22, 24, 25, 26, + 28, 29, 31, 32, 33, 35, 36, 37, + 39, 40, 41, 42, 44, 45, 46, 47, + 48, 50, 51, 52, 53, 54, 56, 57, + 58, 59, 60, 61, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 123, + 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, + 140, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, + 161, 162, 163, 164, 165, 166, 167, 168, + 169, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 177, 178, 179, 180, 181, 182, + 183, 183, 184, 185, 186, 187, 188, 189, + 190, 190, 191, 192, 193, 194, 195, 196, + 196, 197, 198, 199, 200, 201, 202, 202, + 203, 204, 205, 206, 207, 207, 208, 209, + 210, 211, 212, 212, 213, 214, 215, 216, + 217, 217, 218, 219, 220, 221, 222, 222, + 223, 224, 225, 226, 227, 227, 228, 229, + 230, 231, 232, 232, 233, 234, 235, 236, + 236, 237, 238, 239, 240, 240, 241, 242, + 243, 244, 245, 245, 246, 247, 248, 249, + 249, 250, 251, 252, 253, 253, 254, 255, +}; + +static const u16 xgamma8_09[GAMMA8_TABLE_LENGTH] = { + 0, 2, 3, 5, 6, 7, 9, 10, + 11, 13, 14, 15, 16, 18, 19, 20, + 21, 22, 23, 25, 26, 27, 28, 29, + 30, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, + 73, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 126, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, + 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, + 198, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, + 241, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 250, 251, 252, 253, 254, 255, +}; + +static const u16 xgamma8_10[GAMMA8_TABLE_LENGTH] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, +}; + +static const u16 xgamma8_11[GAMMA8_TABLE_LENGTH] = { + 0, 1, 1, 2, 3, 3, 4, 5, + 6, 6, 7, 8, 9, 10, 10, 11, + 12, 13, 14, 15, 16, 16, 17, 18, + 19, 20, 21, 22, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 62, + 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, + 119, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 169, + 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 186, + 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, + 212, 214, 215, 216, 217, 218, 219, 220, + 221, 222, 223, 224, 225, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, + 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 250, 251, 252, 253, 254, 255, +}; + +static const u16 xgamma8_12[GAMMA8_TABLE_LENGTH] = { + 0, 0, 1, 1, 2, 2, 3, 3, + 4, 5, 5, 6, 7, 7, 8, 9, + 9, 10, 11, 11, 12, 13, 13, 14, + 15, 16, 16, 17, 18, 19, 20, 20, + 21, 22, 23, 24, 24, 25, 26, 27, + 28, 28, 29, 30, 31, 32, 33, 34, + 34, 35, 36, 37, 38, 39, 40, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 62, + 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, + 155, 156, 157, 158, 159, 160, 161, 162, + 163, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 175, 176, 177, 178, 179, 180, + 181, 183, 184, 185, 186, 187, 188, 189, + 191, 192, 193, 194, 195, 196, 197, 199, + 200, 201, 202, 203, 204, 205, 207, 208, + 209, 210, 211, 212, 214, 215, 216, 217, + 218, 219, 221, 222, 223, 224, 225, 226, + 228, 229, 230, 231, 232, 234, 235, 236, + 237, 238, 239, 241, 242, 243, 244, 245, + 247, 248, 249, 250, 251, 253, 254, 255, +}; + +static const u16 xgamma8_13[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 1, 1, 2, 2, 2, + 3, 3, 4, 4, 5, 5, 6, 6, + 7, 8, 8, 9, 9, 10, 11, 11, + 12, 12, 13, 14, 14, 15, 16, 16, + 17, 18, 19, 19, 20, 21, 21, 22, + 23, 24, 24, 25, 26, 27, 28, 28, + 29, 30, 31, 31, 32, 33, 34, 35, + 36, 36, 37, 38, 39, 40, 41, 41, + 42, 43, 44, 45, 46, 47, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, + 57, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 112, + 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 135, 136, 137, 138, + 139, 140, 141, 143, 144, 145, 146, 147, + 148, 149, 151, 152, 153, 154, 155, 156, + 157, 159, 160, 161, 162, 163, 164, 166, + 167, 168, 169, 170, 172, 173, 174, 175, + 176, 178, 179, 180, 181, 182, 184, 185, + 186, 187, 188, 190, 191, 192, 193, 194, + 196, 197, 198, 199, 201, 202, 203, 204, + 206, 207, 208, 209, 210, 212, 213, 214, + 215, 217, 218, 219, 220, 222, 223, 224, + 226, 227, 228, 229, 231, 232, 233, 234, + 236, 237, 238, 240, 241, 242, 243, 245, + 246, 247, 249, 250, 251, 252, 254, 255, +}; + +static const u16 xgamma8_14[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 1, 1, 1, 1, 2, + 2, 2, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 7, 8, 8, 9, + 9, 10, 10, 11, 12, 12, 13, 13, + 14, 15, 15, 16, 16, 17, 18, 18, + 19, 20, 20, 21, 22, 22, 23, 24, + 25, 25, 26, 27, 28, 28, 29, 30, + 31, 31, 32, 33, 34, 34, 35, 36, + 37, 38, 38, 39, 40, 41, 42, 43, + 43, 44, 45, 46, 47, 48, 49, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, + 115, 116, 117, 118, 119, 120, 121, 122, + 124, 125, 126, 127, 128, 129, 130, 132, + 133, 134, 135, 136, 137, 139, 140, 141, + 142, 143, 145, 146, 147, 148, 149, 151, + 152, 153, 154, 155, 157, 158, 159, 160, + 161, 163, 164, 165, 166, 168, 169, 170, + 171, 173, 174, 175, 176, 178, 179, 180, + 181, 183, 184, 185, 187, 188, 189, 190, + 192, 193, 194, 196, 197, 198, 200, 201, + 202, 203, 205, 206, 207, 209, 210, 211, + 213, 214, 215, 217, 218, 219, 221, 222, + 223, 225, 226, 227, 229, 230, 232, 233, + 234, 236, 237, 238, 240, 241, 242, 244, + 245, 247, 248, 249, 251, 252, 254, 255, +}; + +static const u16 xgamma8_15[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 1, 1, 1, 1, + 1, 2, 2, 2, 3, 3, 3, 4, + 4, 4, 5, 5, 6, 6, 6, 7, + 7, 8, 8, 9, 9, 10, 10, 11, + 11, 12, 12, 13, 14, 14, 15, 15, + 16, 16, 17, 18, 18, 19, 20, 20, + 21, 21, 22, 23, 23, 24, 25, 26, + 26, 27, 28, 28, 29, 30, 31, 31, + 32, 33, 34, 34, 35, 36, 37, 37, + 38, 39, 40, 41, 41, 42, 43, 44, + 45, 46, 46, 47, 48, 49, 50, 51, + 52, 53, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 65, + 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 112, 113, 114, 115, 116, + 117, 119, 120, 121, 122, 123, 124, 126, + 127, 128, 129, 130, 132, 133, 134, 135, + 136, 138, 139, 140, 141, 142, 144, 145, + 146, 147, 149, 150, 151, 152, 154, 155, + 156, 158, 159, 160, 161, 163, 164, 165, + 167, 168, 169, 171, 172, 173, 174, 176, + 177, 178, 180, 181, 182, 184, 185, 187, + 188, 189, 191, 192, 193, 195, 196, 197, + 199, 200, 202, 203, 204, 206, 207, 209, + 210, 211, 213, 214, 216, 217, 218, 220, + 221, 223, 224, 226, 227, 228, 230, 231, + 233, 234, 236, 237, 239, 240, 242, 243, + 245, 246, 248, 249, 251, 252, 254, 255, +}; + +static const u16 xgamma8_16[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 2, 2, 2, 2, 3, + 3, 3, 4, 4, 4, 5, 5, 5, + 6, 6, 7, 7, 7, 8, 8, 9, + 9, 10, 10, 11, 11, 12, 12, 13, + 13, 14, 14, 15, 15, 16, 16, 17, + 18, 18, 19, 19, 20, 21, 21, 22, + 23, 23, 24, 25, 25, 26, 27, 27, + 28, 29, 29, 30, 31, 31, 32, 33, + 34, 34, 35, 36, 37, 38, 38, 39, + 40, 41, 42, 42, 43, 44, 45, 46, + 46, 47, 48, 49, 50, 51, 52, 53, + 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 97, 98, 99, 100, 101, + 102, 103, 104, 106, 107, 108, 109, 110, + 111, 113, 114, 115, 116, 117, 119, 120, + 121, 122, 123, 125, 126, 127, 128, 130, + 131, 132, 133, 135, 136, 137, 138, 140, + 141, 142, 143, 145, 146, 147, 149, 150, + 151, 153, 154, 155, 157, 158, 159, 161, + 162, 163, 165, 166, 167, 169, 170, 171, + 173, 174, 176, 177, 178, 180, 181, 183, + 184, 185, 187, 188, 190, 191, 193, 194, + 196, 197, 198, 200, 201, 203, 204, 206, + 207, 209, 210, 212, 213, 215, 216, 218, + 219, 221, 222, 224, 225, 227, 228, 230, + 231, 233, 235, 236, 238, 239, 241, 242, + 244, 245, 247, 249, 250, 252, 253, 255, +}; + +static const u16 xgamma8_17[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 2, 2, 2, + 2, 3, 3, 3, 3, 4, 4, 4, + 5, 5, 5, 6, 6, 6, 7, 7, + 7, 8, 8, 9, 9, 10, 10, 10, + 11, 11, 12, 12, 13, 13, 14, 14, + 15, 15, 16, 17, 17, 18, 18, 19, + 19, 20, 21, 21, 22, 22, 23, 24, + 24, 25, 26, 26, 27, 28, 28, 29, + 30, 30, 31, 32, 33, 33, 34, 35, + 36, 36, 37, 38, 39, 39, 40, 41, + 42, 43, 43, 44, 45, 46, 47, 48, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, + 88, 89, 90, 91, 92, 93, 94, 95, + 97, 98, 99, 100, 101, 102, 103, 105, + 106, 107, 108, 109, 111, 112, 113, 114, + 115, 117, 118, 119, 120, 122, 123, 124, + 125, 127, 128, 129, 131, 132, 133, 134, + 136, 137, 138, 140, 141, 142, 144, 145, + 146, 148, 149, 151, 152, 153, 155, 156, + 157, 159, 160, 162, 163, 164, 166, 167, + 169, 170, 172, 173, 174, 176, 177, 179, + 180, 182, 183, 185, 186, 188, 189, 191, + 192, 194, 195, 197, 198, 200, 201, 203, + 205, 206, 208, 209, 211, 212, 214, 216, + 217, 219, 220, 222, 224, 225, 227, 228, + 230, 232, 233, 235, 237, 238, 240, 242, + 243, 245, 247, 248, 250, 252, 253, 255, +}; + +static const u16 xgamma8_18[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 6, + 6, 6, 7, 7, 8, 8, 8, 9, + 9, 10, 10, 10, 11, 11, 12, 12, + 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 21, + 21, 22, 22, 23, 24, 24, 25, 26, + 26, 27, 28, 28, 29, 30, 30, 31, + 32, 32, 33, 34, 35, 35, 36, 37, + 38, 38, 39, 40, 41, 41, 42, 43, + 44, 45, 46, 46, 47, 48, 49, 50, + 51, 52, 53, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 86, 87, 88, 89, 90, + 91, 92, 93, 95, 96, 97, 98, 99, + 100, 102, 103, 104, 105, 107, 108, 109, + 110, 111, 113, 114, 115, 116, 118, 119, + 120, 122, 123, 124, 126, 127, 128, 129, + 131, 132, 134, 135, 136, 138, 139, 140, + 142, 143, 145, 146, 147, 149, 150, 152, + 153, 154, 156, 157, 159, 160, 162, 163, + 165, 166, 168, 169, 171, 172, 174, 175, + 177, 178, 180, 181, 183, 184, 186, 188, + 189, 191, 192, 194, 195, 197, 199, 200, + 202, 204, 205, 207, 208, 210, 212, 213, + 215, 217, 218, 220, 222, 224, 225, 227, + 229, 230, 232, 234, 236, 237, 239, 241, + 243, 244, 246, 248, 250, 251, 253, 255, +}; + +static const u16 xgamma8_19[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 4, 4, 4, 4, 5, + 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, + 11, 11, 12, 12, 12, 13, 13, 14, + 14, 15, 15, 16, 16, 17, 17, 18, + 18, 19, 20, 20, 21, 21, 22, 22, + 23, 24, 24, 25, 26, 26, 27, 28, + 28, 29, 30, 30, 31, 32, 32, 33, + 34, 35, 35, 36, 37, 38, 38, 39, + 40, 41, 41, 42, 43, 44, 45, 46, + 46, 47, 48, 49, 50, 51, 52, 53, + 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 81, 82, 83, 84, 85, + 86, 87, 88, 90, 91, 92, 93, 94, + 95, 97, 98, 99, 100, 101, 103, 104, + 105, 106, 108, 109, 110, 112, 113, 114, + 115, 117, 118, 119, 121, 122, 123, 125, + 126, 127, 129, 130, 132, 133, 134, 136, + 137, 139, 140, 141, 143, 144, 146, 147, + 149, 150, 152, 153, 155, 156, 158, 159, + 161, 162, 164, 165, 167, 168, 170, 172, + 173, 175, 176, 178, 180, 181, 183, 184, + 186, 188, 189, 191, 193, 194, 196, 198, + 199, 201, 203, 204, 206, 208, 210, 211, + 213, 215, 217, 218, 220, 222, 224, 225, + 227, 229, 231, 233, 235, 236, 238, 240, + 242, 244, 246, 247, 249, 251, 253, 255, +}; + +static const u16 xgamma8_20[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 4, 4, + 4, 4, 5, 5, 5, 5, 6, 6, + 6, 7, 7, 7, 8, 8, 8, 9, + 9, 9, 10, 10, 11, 11, 11, 12, + 12, 13, 13, 14, 14, 15, 15, 16, + 16, 17, 17, 18, 18, 19, 19, 20, + 20, 21, 21, 22, 23, 23, 24, 24, + 25, 26, 26, 27, 28, 28, 29, 30, + 30, 31, 32, 32, 33, 34, 35, 35, + 36, 37, 38, 38, 39, 40, 41, 42, + 42, 43, 44, 45, 46, 47, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 84, 85, 86, 87, 88, 89, + 91, 92, 93, 94, 95, 97, 98, 99, + 100, 102, 103, 104, 105, 107, 108, 109, + 111, 112, 113, 115, 116, 117, 119, 120, + 121, 123, 124, 126, 127, 128, 130, 131, + 133, 134, 136, 137, 139, 140, 142, 143, + 145, 146, 148, 149, 151, 152, 154, 155, + 157, 158, 160, 162, 163, 165, 166, 168, + 170, 171, 173, 175, 176, 178, 180, 181, + 183, 185, 186, 188, 190, 192, 193, 195, + 197, 199, 200, 202, 204, 206, 207, 209, + 211, 213, 215, 217, 218, 220, 222, 224, + 226, 228, 230, 232, 233, 235, 237, 239, + 241, 243, 245, 247, 249, 251, 253, 255, +}; + +static const u16 xgamma8_21[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 4, 4, 4, 4, 5, 5, + 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 14, + 14, 14, 15, 15, 16, 16, 17, 17, + 18, 18, 19, 20, 20, 21, 21, 22, + 22, 23, 24, 24, 25, 25, 26, 27, + 27, 28, 29, 29, 30, 31, 31, 32, + 33, 33, 34, 35, 36, 36, 37, 38, + 39, 40, 40, 41, 42, 43, 44, 44, + 45, 46, 47, 48, 49, 50, 51, 51, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 75, 76, + 77, 78, 79, 80, 81, 83, 84, 85, + 86, 87, 88, 90, 91, 92, 93, 95, + 96, 97, 98, 100, 101, 102, 104, 105, + 106, 107, 109, 110, 112, 113, 114, 116, + 117, 118, 120, 121, 123, 124, 126, 127, + 129, 130, 131, 133, 134, 136, 137, 139, + 141, 142, 144, 145, 147, 148, 150, 151, + 153, 155, 156, 158, 160, 161, 163, 165, + 166, 168, 170, 171, 173, 175, 176, 178, + 180, 182, 183, 185, 187, 189, 191, 192, + 194, 196, 198, 200, 202, 203, 205, 207, + 209, 211, 213, 215, 217, 219, 221, 223, + 225, 226, 228, 230, 232, 234, 236, 238, + 241, 243, 245, 247, 249, 251, 253, 255, +}; + +static const u16 xgamma8_22[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 4, 4, 4, + 4, 5, 5, 5, 5, 6, 6, 6, + 6, 7, 7, 7, 8, 8, 8, 9, + 9, 9, 10, 10, 11, 11, 11, 12, + 12, 13, 13, 13, 14, 14, 15, 15, + 16, 16, 17, 17, 18, 18, 19, 19, + 20, 20, 21, 22, 22, 23, 23, 24, + 25, 25, 26, 26, 27, 28, 28, 29, + 30, 30, 31, 32, 33, 33, 34, 35, + 35, 36, 37, 38, 39, 39, 40, 41, + 42, 43, 43, 44, 45, 46, 47, 48, + 49, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 73, 74, 75, 76, 77, 78, 79, 81, + 82, 83, 84, 85, 87, 88, 89, 90, + 91, 93, 94, 95, 97, 98, 99, 100, + 102, 103, 105, 106, 107, 109, 110, 111, + 113, 114, 116, 117, 119, 120, 121, 123, + 124, 126, 127, 129, 130, 132, 133, 135, + 137, 138, 140, 141, 143, 145, 146, 148, + 149, 151, 153, 154, 156, 158, 159, 161, + 163, 165, 166, 168, 170, 172, 173, 175, + 177, 179, 181, 182, 184, 186, 188, 190, + 192, 194, 196, 197, 199, 201, 203, 205, + 207, 209, 211, 213, 215, 217, 219, 221, + 223, 225, 227, 229, 231, 234, 236, 238, + 240, 242, 244, 246, 248, 251, 253, 255, +}; + +static const u16 xgamma8_23[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 7, 7, 7, 7, + 8, 8, 8, 9, 9, 10, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 13, + 14, 14, 15, 15, 16, 16, 17, 17, + 18, 18, 19, 19, 20, 20, 21, 21, + 22, 23, 23, 24, 24, 25, 26, 26, + 27, 28, 28, 29, 30, 30, 31, 32, + 32, 33, 34, 35, 35, 36, 37, 38, + 38, 39, 40, 41, 42, 42, 43, 44, + 45, 46, 47, 48, 49, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, + 69, 70, 71, 72, 73, 74, 75, 76, + 78, 79, 80, 81, 82, 84, 85, 86, + 87, 89, 90, 91, 92, 94, 95, 96, + 98, 99, 100, 102, 103, 104, 106, 107, + 109, 110, 112, 113, 114, 116, 117, 119, + 120, 122, 123, 125, 126, 128, 130, 131, + 133, 134, 136, 138, 139, 141, 143, 144, + 146, 148, 149, 151, 153, 154, 156, 158, + 160, 161, 163, 165, 167, 169, 170, 172, + 174, 176, 178, 180, 182, 183, 185, 187, + 189, 191, 193, 195, 197, 199, 201, 203, + 205, 207, 209, 211, 213, 215, 218, 220, + 222, 224, 226, 228, 230, 233, 235, 237, + 239, 241, 244, 246, 248, 250, 253, 255, +}; + +static const u16 xgamma8_24[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 6, 6, 6, 6, + 7, 7, 7, 8, 8, 8, 9, 9, + 9, 10, 10, 10, 11, 11, 11, 12, + 12, 13, 13, 14, 14, 14, 15, 15, + 16, 16, 17, 17, 18, 18, 19, 19, + 20, 20, 21, 22, 22, 23, 23, 24, + 24, 25, 26, 26, 27, 28, 28, 29, + 30, 30, 31, 32, 32, 33, 34, 35, + 35, 36, 37, 38, 39, 39, 40, 41, + 42, 43, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 53, 54, 55, + 56, 57, 58, 59, 60, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 73, + 74, 75, 76, 77, 78, 80, 81, 82, + 83, 85, 86, 87, 88, 90, 91, 92, + 94, 95, 96, 98, 99, 100, 102, 103, + 105, 106, 108, 109, 111, 112, 114, 115, + 117, 118, 120, 121, 123, 124, 126, 127, + 129, 131, 132, 134, 136, 137, 139, 141, + 142, 144, 146, 148, 149, 151, 153, 155, + 156, 158, 160, 162, 164, 166, 167, 169, + 171, 173, 175, 177, 179, 181, 183, 185, + 187, 189, 191, 193, 195, 197, 199, 201, + 203, 205, 207, 210, 212, 214, 216, 218, + 220, 223, 225, 227, 229, 232, 234, 236, + 239, 241, 243, 246, 248, 250, 253, 255, +}; + +static const u16 xgamma8_25[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 4, 4, + 4, 4, 4, 5, 5, 5, 5, 6, + 6, 6, 6, 7, 7, 7, 7, 8, + 8, 8, 9, 9, 9, 10, 10, 10, + 11, 11, 12, 12, 12, 13, 13, 14, + 14, 15, 15, 15, 16, 16, 17, 17, + 18, 18, 19, 19, 20, 20, 21, 22, + 22, 23, 23, 24, 25, 25, 26, 26, + 27, 28, 28, 29, 30, 30, 31, 32, + 33, 33, 34, 35, 36, 36, 37, 38, + 39, 40, 40, 41, 42, 43, 44, 45, + 46, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 67, 68, 69, + 70, 71, 72, 73, 75, 76, 77, 78, + 80, 81, 82, 83, 85, 86, 87, 89, + 90, 91, 93, 94, 95, 97, 98, 99, + 101, 102, 104, 105, 107, 108, 110, 111, + 113, 114, 116, 117, 119, 121, 122, 124, + 125, 127, 129, 130, 132, 134, 135, 137, + 139, 141, 142, 144, 146, 148, 150, 151, + 153, 155, 157, 159, 161, 163, 165, 166, + 168, 170, 172, 174, 176, 178, 180, 182, + 184, 186, 189, 191, 193, 195, 197, 199, + 201, 204, 206, 208, 210, 212, 215, 217, + 219, 221, 224, 226, 228, 231, 233, 235, + 238, 240, 243, 245, 248, 250, 253, 255, +}; + +static const u16 xgamma8_26[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, + 3, 3, 4, 4, 4, 4, 5, 5, + 5, 5, 5, 6, 6, 6, 6, 7, + 7, 7, 8, 8, 8, 9, 9, 9, + 10, 10, 10, 11, 11, 11, 12, 12, + 13, 13, 13, 14, 14, 15, 15, 16, + 16, 17, 17, 18, 18, 19, 19, 20, + 20, 21, 21, 22, 22, 23, 24, 24, + 25, 25, 26, 27, 27, 28, 29, 29, + 30, 31, 31, 32, 33, 34, 34, 35, + 36, 37, 38, 38, 39, 40, 41, 42, + 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, + 66, 68, 69, 70, 71, 72, 73, 75, + 76, 77, 78, 80, 81, 82, 84, 85, + 86, 88, 89, 90, 92, 93, 94, 96, + 97, 99, 100, 102, 103, 105, 106, 108, + 109, 111, 112, 114, 115, 117, 119, 120, + 122, 124, 125, 127, 129, 130, 132, 134, + 136, 137, 139, 141, 143, 145, 146, 148, + 150, 152, 154, 156, 158, 160, 162, 164, + 166, 168, 170, 172, 174, 176, 178, 180, + 182, 184, 186, 188, 191, 193, 195, 197, + 199, 202, 204, 206, 209, 211, 213, 215, + 218, 220, 223, 225, 227, 230, 232, 235, + 237, 240, 242, 245, 247, 250, 252, 255, +}; + +static const u16 xgamma8_27[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 6, 6, + 6, 6, 7, 7, 7, 7, 8, 8, + 8, 9, 9, 9, 10, 10, 10, 11, + 11, 12, 12, 12, 13, 13, 14, 14, + 14, 15, 15, 16, 16, 17, 17, 18, + 18, 19, 19, 20, 20, 21, 21, 22, + 23, 23, 24, 24, 25, 26, 26, 27, + 28, 28, 29, 30, 30, 31, 32, 33, + 33, 34, 35, 36, 36, 37, 38, 39, + 40, 41, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 51, 52, 53, + 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 68, 69, 70, 71, + 72, 74, 75, 76, 77, 79, 80, 81, + 83, 84, 85, 87, 88, 89, 91, 92, + 94, 95, 97, 98, 100, 101, 103, 104, + 106, 107, 109, 110, 112, 114, 115, 117, + 119, 120, 122, 124, 125, 127, 129, 131, + 132, 134, 136, 138, 140, 141, 143, 145, + 147, 149, 151, 153, 155, 157, 159, 161, + 163, 165, 167, 169, 171, 173, 175, 178, + 180, 182, 184, 186, 188, 191, 193, 195, + 198, 200, 202, 205, 207, 209, 212, 214, + 216, 219, 221, 224, 226, 229, 231, 234, + 237, 239, 242, 244, 247, 250, 252, 255, +}; + +static const u16 xgamma8_28[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, + 7, 8, 8, 8, 9, 9, 9, 10, + 10, 10, 11, 11, 11, 12, 12, 13, + 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 20, + 21, 21, 22, 22, 23, 24, 24, 25, + 25, 26, 27, 27, 28, 29, 29, 30, + 31, 32, 32, 33, 34, 35, 35, 36, + 37, 38, 39, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 50, + 51, 52, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 66, 67, 68, + 69, 70, 72, 73, 74, 75, 77, 78, + 79, 81, 82, 83, 85, 86, 87, 89, + 90, 92, 93, 95, 96, 98, 99, 101, + 102, 104, 105, 107, 109, 110, 112, 114, + 115, 117, 119, 120, 122, 124, 126, 127, + 129, 131, 133, 135, 137, 138, 140, 142, + 144, 146, 148, 150, 152, 154, 156, 158, + 160, 162, 164, 167, 169, 171, 173, 175, + 177, 180, 182, 184, 186, 189, 191, 193, + 196, 198, 200, 203, 205, 208, 210, 213, + 215, 218, 220, 223, 225, 228, 231, 233, + 236, 239, 241, 244, 247, 249, 252, 255, +}; + +static const u16 xgamma8_29[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 6, 6, 6, 6, + 7, 7, 7, 7, 8, 8, 8, 9, + 9, 9, 9, 10, 10, 11, 11, 11, + 12, 12, 12, 13, 13, 14, 14, 15, + 15, 15, 16, 16, 17, 17, 18, 18, + 19, 19, 20, 21, 21, 22, 22, 23, + 23, 24, 25, 25, 26, 27, 27, 28, + 29, 29, 30, 31, 32, 32, 33, 34, + 35, 35, 36, 37, 38, 39, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 64, 65, + 66, 67, 68, 70, 71, 72, 73, 75, + 76, 77, 79, 80, 81, 83, 84, 86, + 87, 88, 90, 91, 93, 94, 96, 97, + 99, 101, 102, 104, 105, 107, 109, 110, + 112, 114, 115, 117, 119, 121, 122, 124, + 126, 128, 130, 132, 134, 135, 137, 139, + 141, 143, 145, 147, 149, 151, 153, 155, + 158, 160, 162, 164, 166, 168, 171, 173, + 175, 177, 180, 182, 184, 187, 189, 191, + 194, 196, 199, 201, 204, 206, 209, 211, + 214, 216, 219, 222, 224, 227, 230, 232, + 235, 238, 241, 244, 246, 249, 252, 255, +}; + +static const u16 xgamma8_30[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 4, 4, + 4, 4, 4, 5, 5, 5, 5, 6, + 6, 6, 6, 6, 7, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 10, 10, + 10, 11, 11, 12, 12, 12, 13, 13, + 14, 14, 14, 15, 15, 16, 16, 17, + 17, 18, 18, 19, 19, 20, 20, 21, + 22, 22, 23, 23, 24, 25, 25, 26, + 27, 27, 28, 29, 29, 30, 31, 32, + 32, 33, 34, 35, 35, 36, 37, 38, + 39, 40, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 60, 61, 62, + 63, 64, 65, 67, 68, 69, 70, 72, + 73, 74, 76, 77, 78, 80, 81, 82, + 84, 85, 87, 88, 90, 91, 93, 94, + 96, 97, 99, 101, 102, 104, 105, 107, + 109, 111, 112, 114, 116, 118, 119, 121, + 123, 125, 127, 129, 131, 132, 134, 136, + 138, 140, 142, 144, 147, 149, 151, 153, + 155, 157, 159, 162, 164, 166, 168, 171, + 173, 175, 178, 180, 182, 185, 187, 190, + 192, 195, 197, 200, 202, 205, 207, 210, + 213, 215, 218, 221, 223, 226, 229, 232, + 235, 237, 240, 243, 246, 249, 252, 255, +}; + +static const u16 xgamma8_31[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 5, 5, + 5, 5, 6, 6, 6, 6, 6, 7, + 7, 7, 8, 8, 8, 8, 9, 9, + 9, 10, 10, 10, 11, 11, 12, 12, + 12, 13, 13, 14, 14, 14, 15, 15, + 16, 16, 17, 17, 18, 18, 19, 19, + 20, 20, 21, 22, 22, 23, 23, 24, + 25, 25, 26, 27, 27, 28, 29, 29, + 30, 31, 32, 32, 33, 34, 35, 36, + 36, 37, 38, 39, 40, 41, 42, 42, + 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 56, 57, 58, 59, + 60, 61, 62, 64, 65, 66, 67, 69, + 70, 71, 73, 74, 75, 77, 78, 79, + 81, 82, 84, 85, 87, 88, 90, 91, + 93, 94, 96, 97, 99, 101, 102, 104, + 106, 108, 109, 111, 113, 115, 116, 118, + 120, 122, 124, 126, 128, 130, 132, 134, + 136, 138, 140, 142, 144, 146, 148, 150, + 152, 155, 157, 159, 161, 164, 166, 168, + 171, 173, 175, 178, 180, 183, 185, 188, + 190, 193, 195, 198, 201, 203, 206, 209, + 211, 214, 217, 220, 222, 225, 228, 231, + 234, 237, 240, 243, 246, 249, 252, 255, +}; + +static const u16 xgamma8_32[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, + 4, 5, 5, 5, 5, 6, 6, 6, + 6, 6, 7, 7, 7, 8, 8, 8, + 8, 9, 9, 9, 10, 10, 10, 11, + 11, 12, 12, 12, 13, 13, 14, 14, + 14, 15, 15, 16, 16, 17, 17, 18, + 18, 19, 19, 20, 21, 21, 22, 22, + 23, 23, 24, 25, 25, 26, 27, 27, + 28, 29, 30, 30, 31, 32, 33, 33, + 34, 35, 36, 37, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, + 57, 59, 60, 61, 62, 63, 65, 66, + 67, 68, 70, 71, 72, 74, 75, 76, + 78, 79, 81, 82, 84, 85, 87, 88, + 90, 91, 93, 95, 96, 98, 99, 101, + 103, 105, 106, 108, 110, 112, 113, 115, + 117, 119, 121, 123, 125, 127, 129, 131, + 133, 135, 137, 139, 141, 143, 146, 148, + 150, 152, 154, 157, 159, 161, 164, 166, + 168, 171, 173, 176, 178, 181, 183, 186, + 188, 191, 194, 196, 199, 202, 204, 207, + 210, 213, 216, 219, 221, 224, 227, 230, + 233, 236, 239, 242, 246, 249, 252, 255, +}; + +static const u16 xgamma8_33[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 4, 4, + 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 10, + 10, 11, 11, 11, 12, 12, 12, 13, + 13, 14, 14, 15, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 21, + 21, 22, 22, 23, 24, 24, 25, 26, + 26, 27, 28, 28, 29, 30, 31, 31, + 32, 33, 34, 34, 35, 36, 37, 38, + 39, 40, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 53, 54, + 55, 56, 57, 58, 59, 61, 62, 63, + 64, 66, 67, 68, 70, 71, 72, 74, + 75, 76, 78, 79, 81, 82, 84, 85, + 87, 88, 90, 92, 93, 95, 97, 98, + 100, 102, 103, 105, 107, 109, 111, 113, + 114, 116, 118, 120, 122, 124, 126, 128, + 130, 132, 134, 136, 139, 141, 143, 145, + 147, 150, 152, 154, 157, 159, 161, 164, + 166, 169, 171, 174, 176, 179, 181, 184, + 187, 189, 192, 195, 198, 200, 203, 206, + 209, 212, 215, 217, 220, 223, 226, 230, + 233, 236, 239, 242, 245, 248, 252, 255, +}; + +static const u16 xgamma8_34[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 3, + 3, 4, 4, 4, 4, 4, 5, 5, + 5, 5, 5, 6, 6, 6, 6, 7, + 7, 7, 7, 8, 8, 8, 9, 9, + 9, 10, 10, 10, 11, 11, 11, 12, + 12, 12, 13, 13, 14, 14, 15, 15, + 16, 16, 17, 17, 18, 18, 19, 19, + 20, 20, 21, 21, 22, 23, 23, 24, + 24, 25, 26, 26, 27, 28, 29, 29, + 30, 31, 32, 32, 33, 34, 35, 36, + 37, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 55, 56, 57, 58, 59, 60, + 62, 63, 64, 66, 67, 68, 70, 71, + 72, 74, 75, 77, 78, 80, 81, 83, + 84, 86, 87, 89, 90, 92, 94, 95, + 97, 99, 101, 102, 104, 106, 108, 110, + 112, 114, 115, 117, 119, 121, 123, 125, + 128, 130, 132, 134, 136, 138, 141, 143, + 145, 147, 150, 152, 154, 157, 159, 162, + 164, 167, 169, 172, 174, 177, 180, 182, + 185, 188, 190, 193, 196, 199, 202, 205, + 208, 210, 213, 216, 219, 223, 226, 229, + 232, 235, 238, 242, 245, 248, 252, 255, +}; + +static const u16 xgamma8_35[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, + 4, 5, 5, 5, 5, 5, 6, 6, + 6, 6, 7, 7, 7, 7, 8, 8, + 8, 9, 9, 9, 10, 10, 10, 11, + 11, 11, 12, 12, 13, 13, 13, 14, + 14, 15, 15, 16, 16, 17, 17, 18, + 18, 19, 19, 20, 20, 21, 22, 22, + 23, 23, 24, 25, 25, 26, 27, 28, + 28, 29, 30, 30, 31, 32, 33, 34, + 35, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 56, 57, 58, + 59, 60, 62, 63, 64, 66, 67, 68, + 70, 71, 72, 74, 75, 77, 78, 80, + 81, 83, 85, 86, 88, 89, 91, 93, + 94, 96, 98, 100, 102, 103, 105, 107, + 109, 111, 113, 115, 117, 119, 121, 123, + 125, 127, 129, 131, 134, 136, 138, 140, + 143, 145, 147, 150, 152, 155, 157, 159, + 162, 165, 167, 170, 172, 175, 178, 180, + 183, 186, 189, 192, 194, 197, 200, 203, + 206, 209, 212, 215, 219, 222, 225, 228, + 231, 235, 238, 241, 245, 248, 252, 255, +}; + +static const u16 xgamma8_36[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 4, 4, + 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 8, 9, 9, 9, 10, + 10, 10, 11, 11, 12, 12, 12, 13, + 13, 14, 14, 15, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 20, 20, 21, + 21, 22, 23, 23, 24, 24, 25, 26, + 27, 27, 28, 29, 29, 30, 31, 32, + 33, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 42, 43, 44, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 56, + 57, 58, 59, 61, 62, 63, 64, 66, + 67, 69, 70, 71, 73, 74, 76, 77, + 79, 80, 82, 83, 85, 87, 88, 90, + 92, 94, 95, 97, 99, 101, 103, 104, + 106, 108, 110, 112, 114, 116, 118, 120, + 122, 125, 127, 129, 131, 133, 136, 138, + 140, 143, 145, 147, 150, 152, 155, 157, + 160, 162, 165, 168, 170, 173, 176, 179, + 181, 184, 187, 190, 193, 196, 199, 202, + 205, 208, 211, 214, 218, 221, 224, 227, + 231, 234, 237, 241, 244, 248, 251, 255, +}; + +static const u16 xgamma8_37[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 3, + 3, 4, 4, 4, 4, 4, 5, 5, + 5, 5, 5, 6, 6, 6, 6, 7, + 7, 7, 7, 8, 8, 8, 9, 9, + 9, 10, 10, 10, 11, 11, 11, 12, + 12, 13, 13, 13, 14, 14, 15, 15, + 16, 16, 17, 17, 18, 18, 19, 19, + 20, 20, 21, 22, 22, 23, 24, 24, + 25, 26, 26, 27, 28, 28, 29, 30, + 31, 32, 32, 33, 34, 35, 36, 37, + 38, 39, 39, 40, 41, 42, 43, 44, + 45, 47, 48, 49, 50, 51, 52, 53, + 54, 56, 57, 58, 59, 61, 62, 63, + 65, 66, 67, 69, 70, 72, 73, 75, + 76, 78, 79, 81, 83, 84, 86, 88, + 89, 91, 93, 95, 96, 98, 100, 102, + 104, 106, 108, 110, 112, 114, 116, 118, + 120, 122, 124, 127, 129, 131, 133, 136, + 138, 140, 143, 145, 148, 150, 153, 155, + 158, 160, 163, 166, 169, 171, 174, 177, + 180, 183, 186, 188, 191, 194, 198, 201, + 204, 207, 210, 213, 217, 220, 223, 227, + 230, 233, 237, 241, 244, 248, 251, 255, +}; + +static const u16 xgamma8_38[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, + 4, 5, 5, 5, 5, 6, 6, 6, + 6, 6, 7, 7, 7, 8, 8, 8, + 8, 9, 9, 9, 10, 10, 10, 11, + 11, 12, 12, 12, 13, 13, 14, 14, + 15, 15, 15, 16, 16, 17, 18, 18, + 19, 19, 20, 20, 21, 21, 22, 23, + 23, 24, 25, 25, 26, 27, 28, 28, + 29, 30, 31, 31, 32, 33, 34, 35, + 36, 37, 38, 38, 39, 40, 41, 42, + 43, 44, 45, 47, 48, 49, 50, 51, + 52, 53, 55, 56, 57, 58, 60, 61, + 62, 64, 65, 66, 68, 69, 71, 72, + 74, 75, 77, 78, 80, 82, 83, 85, + 87, 88, 90, 92, 94, 96, 98, 99, + 101, 103, 105, 107, 109, 111, 113, 115, + 118, 120, 122, 124, 126, 129, 131, 133, + 136, 138, 141, 143, 146, 148, 151, 153, + 156, 158, 161, 164, 167, 169, 172, 175, + 178, 181, 184, 187, 190, 193, 196, 199, + 203, 206, 209, 212, 216, 219, 222, 226, + 229, 233, 237, 240, 244, 247, 251, 255, +}; + +static const u16 xgamma8_39[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 4, 4, 4, + 4, 4, 4, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, + 10, 11, 11, 11, 12, 12, 13, 13, + 13, 14, 14, 15, 15, 16, 16, 17, + 17, 18, 18, 19, 20, 20, 21, 21, + 22, 23, 23, 24, 25, 25, 26, 27, + 27, 28, 29, 30, 31, 31, 32, 33, + 34, 35, 36, 37, 38, 38, 39, 40, + 41, 42, 43, 45, 46, 47, 48, 49, + 50, 51, 52, 54, 55, 56, 57, 59, + 60, 61, 63, 64, 66, 67, 68, 70, + 71, 73, 74, 76, 78, 79, 81, 83, + 84, 86, 88, 90, 91, 93, 95, 97, + 99, 101, 103, 105, 107, 109, 111, 113, + 115, 117, 120, 122, 124, 126, 129, 131, + 133, 136, 138, 141, 143, 146, 149, 151, + 154, 157, 159, 162, 165, 168, 171, 173, + 176, 179, 182, 185, 189, 192, 195, 198, + 201, 205, 208, 211, 215, 218, 222, 225, + 229, 232, 236, 240, 243, 247, 251, 255, +}; + +static const u16 xgamma8_40[GAMMA8_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 5, 5, 5, + 5, 5, 6, 6, 6, 6, 7, 7, + 7, 7, 8, 8, 8, 9, 9, 9, + 9, 10, 10, 11, 11, 11, 12, 12, + 13, 13, 13, 14, 14, 15, 15, 16, + 16, 17, 17, 18, 18, 19, 19, 20, + 21, 21, 22, 23, 23, 24, 25, 25, + 26, 27, 27, 28, 29, 30, 31, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 52, 53, 54, 55, 57, + 58, 59, 61, 62, 63, 65, 66, 68, + 69, 71, 72, 74, 75, 77, 79, 80, + 82, 84, 85, 87, 89, 91, 93, 95, + 96, 98, 100, 102, 104, 107, 109, 111, + 113, 115, 117, 120, 122, 124, 126, 129, + 131, 134, 136, 139, 141, 144, 146, 149, + 152, 155, 157, 160, 163, 166, 169, 172, + 175, 178, 181, 184, 187, 190, 194, 197, + 200, 203, 207, 210, 214, 217, 221, 224, + 228, 232, 236, 239, 243, 247, 251, 255, +}; + +static const u16 *xgamma8_curves[GAMMA_CURVE_LENGTH] = { + &xgamma8_01[0], + &xgamma8_02[0], + &xgamma8_03[0], + &xgamma8_04[0], + &xgamma8_05[0], + &xgamma8_06[0], + &xgamma8_07[0], + &xgamma8_08[0], + &xgamma8_09[0], + &xgamma8_10[0], + &xgamma8_11[0], + &xgamma8_12[0], + &xgamma8_13[0], + &xgamma8_14[0], + &xgamma8_15[0], + &xgamma8_16[0], + &xgamma8_17[0], + &xgamma8_18[0], + &xgamma8_19[0], + &xgamma8_20[0], + &xgamma8_21[0], + &xgamma8_22[0], + &xgamma8_23[0], + &xgamma8_24[0], + &xgamma8_25[0], + &xgamma8_26[0], + &xgamma8_27[0], + &xgamma8_28[0], + &xgamma8_29[0], + &xgamma8_30[0], + &xgamma8_31[0], + &xgamma8_32[0], + &xgamma8_33[0], + &xgamma8_34[0], + &xgamma8_35[0], + &xgamma8_36[0], + &xgamma8_37[0], + &xgamma8_38[0], + &xgamma8_39[0], + &xgamma8_40[0], +}; + +#define GAMMA_BPC_10 (10) +#define GAMMA10_TABLE_LENGTH BIT(GAMMA_BPC_10) +static const u16 xgamma10_01[GAMMA10_TABLE_LENGTH] = { + 0, 512, 548, 571, 588, 601, 612, 621, 630, 637, 644, + 650, 656, 661, 666, 671, 675, 679, 683, 687, 690, 694, + 697, 700, 703, 706, 709, 711, 714, 716, 719, 721, 723, + 726, 728, 730, 732, 734, 736, 738, 740, 742, 743, 745, + 747, 749, 750, 752, 753, 755, 756, 758, 759, 761, 762, + 764, 765, 766, 768, 769, 770, 772, 773, 774, 775, 777, + 778, 779, 780, 781, 782, 783, 785, 786, 787, 788, 789, + 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, + 800, 801, 802, 803, 804, 805, 806, 807, 807, 808, 809, + 810, 811, 812, 812, 813, 814, 815, 815, 816, 817, 818, + 819, 819, 820, 821, 821, 822, 823, 824, 824, 825, 826, + 826, 827, 828, 828, 829, 830, 830, 831, 832, 832, 833, + 834, 834, 835, 835, 836, 837, 837, 838, 838, 839, 840, + 840, 841, 841, 842, 843, 843, 844, 844, 845, 845, 846, + 847, 847, 848, 848, 849, 849, 850, 850, 851, 851, 852, + 852, 853, 853, 854, 854, 855, 855, 856, 856, 857, 857, + 858, 858, 859, 859, 860, 860, 861, 861, 862, 862, 863, + 863, 864, 864, 864, 865, 865, 866, 866, 867, 867, 868, + 868, 869, 869, 869, 870, 870, 871, 871, 872, 872, 872, + 873, 873, 874, 874, 874, 875, 875, 876, 876, 876, 877, + 877, 878, 878, 878, 879, 879, 880, 880, 880, 881, 881, + 882, 882, 882, 883, 883, 883, 884, 884, 885, 885, 885, + 886, 886, 886, 887, 887, 887, 888, 888, 889, 889, 889, + 890, 890, 890, 891, 891, 891, 892, 892, 892, 893, 893, + 893, 894, 894, 894, 895, 895, 895, 896, 896, 896, 897, + 897, 897, 898, 898, 898, 899, 899, 899, 900, 900, 900, + 901, 901, 901, 902, 902, 902, 902, 903, 903, 903, 904, + 904, 904, 905, 905, 905, 906, 906, 906, 906, 907, 907, + 907, 908, 908, 908, 908, 909, 909, 909, 910, 910, 910, + 910, 911, 911, 911, 912, 912, 912, 912, 913, 913, 913, + 914, 914, 914, 914, 915, 915, 915, 915, 916, 916, 916, + 917, 917, 917, 917, 918, 918, 918, 918, 919, 919, 919, + 919, 920, 920, 920, 921, 921, 921, 921, 922, 922, 922, + 922, 923, 923, 923, 923, 924, 924, 924, 924, 925, 925, + 925, 925, 926, 926, 926, 926, 927, 927, 927, 927, 928, + 928, 928, 928, 928, 929, 929, 929, 929, 930, 930, 930, + 930, 931, 931, 931, 931, 932, 932, 932, 932, 932, 933, + 933, 933, 933, 934, 934, 934, 934, 935, 935, 935, 935, + 935, 936, 936, 936, 936, 937, 937, 937, 937, 937, 938, + 938, 938, 938, 939, 939, 939, 939, 939, 940, 940, 940, + 940, 940, 941, 941, 941, 941, 942, 942, 942, 942, 942, + 943, 943, 943, 943, 943, 944, 944, 944, 944, 944, 945, + 945, 945, 945, 945, 946, 946, 946, 946, 946, 947, 947, + 947, 947, 947, 948, 948, 948, 948, 948, 949, 949, 949, + 949, 949, 950, 950, 950, 950, 950, 951, 951, 951, 951, + 951, 952, 952, 952, 952, 952, 953, 953, 953, 953, 953, + 953, 954, 954, 954, 954, 954, 955, 955, 955, 955, 955, + 956, 956, 956, 956, 956, 956, 957, 957, 957, 957, 957, + 958, 958, 958, 958, 958, 958, 959, 959, 959, 959, 959, + 960, 960, 960, 960, 960, 960, 961, 961, 961, 961, 961, + 961, 962, 962, 962, 962, 962, 962, 963, 963, 963, 963, + 963, 964, 964, 964, 964, 964, 964, 965, 965, 965, 965, + 965, 965, 966, 966, 966, 966, 966, 966, 967, 967, 967, + 967, 967, 967, 968, 968, 968, 968, 968, 968, 969, 969, + 969, 969, 969, 969, 970, 970, 970, 970, 970, 970, 970, + 971, 971, 971, 971, 971, 971, 972, 972, 972, 972, 972, + 972, 973, 973, 973, 973, 973, 973, 974, 974, 974, 974, + 974, 974, 974, 975, 975, 975, 975, 975, 975, 976, 976, + 976, 976, 976, 976, 976, 977, 977, 977, 977, 977, 977, + 977, 978, 978, 978, 978, 978, 978, 979, 979, 979, 979, + 979, 979, 979, 980, 980, 980, 980, 980, 980, 980, 981, + 981, 981, 981, 981, 981, 981, 982, 982, 982, 982, 982, + 982, 982, 983, 983, 983, 983, 983, 983, 983, 984, 984, + 984, 984, 984, 984, 984, 985, 985, 985, 985, 985, 985, + 985, 986, 986, 986, 986, 986, 986, 986, 987, 987, 987, + 987, 987, 987, 987, 988, 988, 988, 988, 988, 988, 988, + 989, 989, 989, 989, 989, 989, 989, 989, 990, 990, 990, + 990, 990, 990, 990, 991, 991, 991, 991, 991, 991, 991, + 991, 992, 992, 992, 992, 992, 992, 992, 993, 993, 993, + 993, 993, 993, 993, 993, 994, 994, 994, 994, 994, 994, + 994, 994, 995, 995, 995, 995, 995, 995, 995, 996, 996, + 996, 996, 996, 996, 996, 996, 997, 997, 997, 997, 997, + 997, 997, 997, 998, 998, 998, 998, 998, 998, 998, 998, + 999, 999, 999, 999, 999, 999, 999, 999, 1000, 1000, 1000, + 1000, 1000, 1000, 1000, 1000, 1000, 1001, 1001, 1001, 1001, 1001, + 1001, 1001, 1001, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, + 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1004, 1004, 1004, + 1004, 1004, 1004, 1004, 1004, 1004, 1005, 1005, 1005, 1005, 1005, + 1005, 1005, 1005, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, + 1006, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1008, + 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1009, 1009, 1009, 1009, + 1009, 1009, 1009, 1009, 1009, 1010, 1010, 1010, 1010, 1010, 1010, + 1010, 1010, 1010, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, + 1011, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1013, + 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1014, 1014, 1014, + 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1015, 1015, 1015, 1015, + 1015, 1015, 1015, 1015, 1015, 1016, 1016, 1016, 1016, 1016, 1016, + 1016, 1016, 1016, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, + 1017, 1017, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, + 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1020, + 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1021, 1021, + 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1022, 1022, 1022, + 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1023, 1023, 1023, 1023, + 1023, +}; + +static const u16 xgamma10_02[GAMMA10_TABLE_LENGTH] = { + 0, 256, 294, 319, 338, 353, 366, 378, 388, 397, 405, + 413, 420, 427, 434, 440, 445, 451, 456, 461, 466, 470, + 475, 479, 483, 487, 491, 495, 498, 502, 505, 508, 512, + 515, 518, 521, 524, 527, 529, 532, 535, 538, 540, 543, + 545, 548, 550, 552, 555, 557, 559, 562, 564, 566, 568, + 570, 572, 574, 576, 578, 580, 582, 584, 586, 588, 589, + 591, 593, 595, 597, 598, 600, 602, 603, 605, 607, 608, + 610, 611, 613, 614, 616, 618, 619, 621, 622, 623, 625, + 626, 628, 629, 631, 632, 633, 635, 636, 637, 639, 640, + 641, 643, 644, 645, 646, 648, 649, 650, 651, 653, 654, + 655, 656, 657, 658, 660, 661, 662, 663, 664, 665, 666, + 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, + 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, + 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, + 700, 701, 702, 703, 704, 705, 706, 707, 708, 708, 709, + 710, 711, 712, 713, 714, 714, 715, 716, 717, 718, 719, + 719, 720, 721, 722, 723, 723, 724, 725, 726, 727, 727, + 728, 729, 730, 731, 731, 732, 733, 734, 734, 735, 736, + 737, 737, 738, 739, 740, 740, 741, 742, 742, 743, 744, + 745, 745, 746, 747, 747, 748, 749, 750, 750, 751, 752, + 752, 753, 754, 754, 755, 756, 756, 757, 758, 758, 759, + 760, 760, 761, 762, 762, 763, 764, 764, 765, 765, 766, + 767, 767, 768, 769, 769, 770, 771, 771, 772, 772, 773, + 774, 774, 775, 775, 776, 777, 777, 778, 778, 779, 780, + 780, 781, 781, 782, 783, 783, 784, 784, 785, 785, 786, + 787, 787, 788, 788, 789, 789, 790, 791, 791, 792, 792, + 793, 793, 794, 794, 795, 796, 796, 797, 797, 798, 798, + 799, 799, 800, 800, 801, 801, 802, 803, 803, 804, 804, + 805, 805, 806, 806, 807, 807, 808, 808, 809, 809, 810, + 810, 811, 811, 812, 812, 813, 813, 814, 814, 815, 815, + 816, 816, 817, 817, 818, 818, 819, 819, 820, 820, 821, + 821, 822, 822, 823, 823, 824, 824, 825, 825, 825, 826, + 826, 827, 827, 828, 828, 829, 829, 830, 830, 831, 831, + 832, 832, 832, 833, 833, 834, 834, 835, 835, 836, 836, + 837, 837, 837, 838, 838, 839, 839, 840, 840, 841, 841, + 841, 842, 842, 843, 843, 844, 844, 844, 845, 845, 846, + 846, 847, 847, 847, 848, 848, 849, 849, 850, 850, 850, + 851, 851, 852, 852, 852, 853, 853, 854, 854, 855, 855, + 855, 856, 856, 857, 857, 857, 858, 858, 859, 859, 859, + 860, 860, 861, 861, 861, 862, 862, 863, 863, 863, 864, + 864, 865, 865, 865, 866, 866, 866, 867, 867, 868, 868, + 868, 869, 869, 870, 870, 870, 871, 871, 871, 872, 872, + 873, 873, 873, 874, 874, 875, 875, 875, 876, 876, 876, + 877, 877, 877, 878, 878, 879, 879, 879, 880, 880, 880, + 881, 881, 882, 882, 882, 883, 883, 883, 884, 884, 884, + 885, 885, 885, 886, 886, 887, 887, 887, 888, 888, 888, + 889, 889, 889, 890, 890, 890, 891, 891, 891, 892, 892, + 892, 893, 893, 894, 894, 894, 895, 895, 895, 896, 896, + 896, 897, 897, 897, 898, 898, 898, 899, 899, 899, 900, + 900, 900, 901, 901, 901, 902, 902, 902, 903, 903, 903, + 904, 904, 904, 905, 905, 905, 906, 906, 906, 907, 907, + 907, 908, 908, 908, 908, 909, 909, 909, 910, 910, 910, + 911, 911, 911, 912, 912, 912, 913, 913, 913, 914, 914, + 914, 914, 915, 915, 915, 916, 916, 916, 917, 917, 917, + 918, 918, 918, 919, 919, 919, 919, 920, 920, 920, 921, + 921, 921, 922, 922, 922, 923, 923, 923, 923, 924, 924, + 924, 925, 925, 925, 926, 926, 926, 926, 927, 927, 927, + 928, 928, 928, 928, 929, 929, 929, 930, 930, 930, 931, + 931, 931, 931, 932, 932, 932, 933, 933, 933, 933, 934, + 934, 934, 935, 935, 935, 935, 936, 936, 936, 937, 937, + 937, 937, 938, 938, 938, 939, 939, 939, 939, 940, 940, + 940, 941, 941, 941, 941, 942, 942, 942, 942, 943, 943, + 943, 944, 944, 944, 944, 945, 945, 945, 946, 946, 946, + 946, 947, 947, 947, 947, 948, 948, 948, 949, 949, 949, + 949, 950, 950, 950, 950, 951, 951, 951, 951, 952, 952, + 952, 953, 953, 953, 953, 954, 954, 954, 954, 955, 955, + 955, 955, 956, 956, 956, 956, 957, 957, 957, 958, 958, + 958, 958, 959, 959, 959, 959, 960, 960, 960, 960, 961, + 961, 961, 961, 962, 962, 962, 962, 963, 963, 963, 963, + 964, 964, 964, 964, 965, 965, 965, 965, 966, 966, 966, + 966, 967, 967, 967, 967, 968, 968, 968, 968, 969, 969, + 969, 969, 970, 970, 970, 970, 971, 971, 971, 971, 972, + 972, 972, 972, 973, 973, 973, 973, 974, 974, 974, 974, + 975, 975, 975, 975, 976, 976, 976, 976, 977, 977, 977, + 977, 978, 978, 978, 978, 978, 979, 979, 979, 979, 980, + 980, 980, 980, 981, 981, 981, 981, 982, 982, 982, 982, + 983, 983, 983, 983, 983, 984, 984, 984, 984, 985, 985, + 985, 985, 986, 986, 986, 986, 986, 987, 987, 987, 987, + 988, 988, 988, 988, 989, 989, 989, 989, 989, 990, 990, + 990, 990, 991, 991, 991, 991, 992, 992, 992, 992, 992, + 993, 993, 993, 993, 994, 994, 994, 994, 994, 995, 995, + 995, 995, 996, 996, 996, 996, 996, 997, 997, 997, 997, + 998, 998, 998, 998, 998, 999, 999, 999, 999, 1000, 1000, + 1000, 1000, 1000, 1001, 1001, 1001, 1001, 1002, 1002, 1002, 1002, + 1002, 1003, 1003, 1003, 1003, 1003, 1004, 1004, 1004, 1004, 1005, + 1005, 1005, 1005, 1005, 1006, 1006, 1006, 1006, 1006, 1007, 1007, + 1007, 1007, 1008, 1008, 1008, 1008, 1008, 1009, 1009, 1009, 1009, + 1009, 1010, 1010, 1010, 1010, 1010, 1011, 1011, 1011, 1011, 1012, + 1012, 1012, 1012, 1012, 1013, 1013, 1013, 1013, 1013, 1014, 1014, + 1014, 1014, 1014, 1015, 1015, 1015, 1015, 1015, 1016, 1016, 1016, + 1016, 1017, 1017, 1017, 1017, 1017, 1018, 1018, 1018, 1018, 1018, + 1019, 1019, 1019, 1019, 1019, 1020, 1020, 1020, 1020, 1020, 1021, + 1021, 1021, 1021, 1021, 1022, 1022, 1022, 1022, 1022, 1023, 1023, + 1023, +}; + +static const u16 xgamma10_03[GAMMA10_TABLE_LENGTH] = { + 0, 128, 157, 178, 194, 207, 219, 229, 239, 247, 255, + 263, 270, 276, 282, 288, 294, 299, 304, 309, 314, 319, + 323, 328, 332, 336, 340, 344, 348, 351, 355, 358, 362, + 365, 368, 372, 375, 378, 381, 384, 387, 390, 393, 395, + 398, 401, 403, 406, 409, 411, 414, 416, 419, 421, 423, + 426, 428, 430, 432, 435, 437, 439, 441, 443, 445, 447, + 450, 452, 454, 456, 458, 460, 461, 463, 465, 467, 469, + 471, 473, 474, 476, 478, 480, 482, 483, 485, 487, 488, + 490, 492, 493, 495, 497, 498, 500, 501, 503, 505, 506, + 508, 509, 511, 512, 514, 515, 517, 518, 520, 521, 523, + 524, 525, 527, 528, 530, 531, 532, 534, 535, 537, 538, + 539, 541, 542, 543, 544, 546, 547, 548, 550, 551, 552, + 553, 555, 556, 557, 558, 560, 561, 562, 563, 565, 566, + 567, 568, 569, 570, 572, 573, 574, 575, 576, 577, 579, + 580, 581, 582, 583, 584, 585, 586, 587, 589, 590, 591, + 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, + 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, + 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, + 625, 626, 627, 628, 629, 630, 631, 632, 633, 633, 634, + 635, 636, 637, 638, 639, 640, 641, 642, 642, 643, 644, + 645, 646, 647, 648, 649, 649, 650, 651, 652, 653, 654, + 655, 655, 656, 657, 658, 659, 660, 661, 661, 662, 663, + 664, 665, 665, 666, 667, 668, 669, 670, 670, 671, 672, + 673, 674, 674, 675, 676, 677, 677, 678, 679, 680, 681, + 681, 682, 683, 684, 684, 685, 686, 687, 688, 688, 689, + 690, 691, 691, 692, 693, 694, 694, 695, 696, 696, 697, + 698, 699, 699, 700, 701, 702, 702, 703, 704, 704, 705, + 706, 707, 707, 708, 709, 709, 710, 711, 712, 712, 713, + 714, 714, 715, 716, 716, 717, 718, 718, 719, 720, 721, + 721, 722, 723, 723, 724, 725, 725, 726, 727, 727, 728, + 729, 729, 730, 731, 731, 732, 733, 733, 734, 734, 735, + 736, 736, 737, 738, 738, 739, 740, 740, 741, 742, 742, + 743, 743, 744, 745, 745, 746, 747, 747, 748, 748, 749, + 750, 750, 751, 752, 752, 753, 753, 754, 755, 755, 756, + 756, 757, 758, 758, 759, 759, 760, 761, 761, 762, 762, + 763, 764, 764, 765, 765, 766, 767, 767, 768, 768, 769, + 770, 770, 771, 771, 772, 772, 773, 774, 774, 775, 775, + 776, 776, 777, 778, 778, 779, 779, 780, 780, 781, 782, + 782, 783, 783, 784, 784, 785, 785, 786, 787, 787, 788, + 788, 789, 789, 790, 790, 791, 792, 792, 793, 793, 794, + 794, 795, 795, 796, 796, 797, 797, 798, 799, 799, 800, + 800, 801, 801, 802, 802, 803, 803, 804, 804, 805, 805, + 806, 806, 807, 808, 808, 809, 809, 810, 810, 811, 811, + 812, 812, 813, 813, 814, 814, 815, 815, 816, 816, 817, + 817, 818, 818, 819, 819, 820, 820, 821, 821, 822, 822, + 823, 823, 824, 824, 825, 825, 826, 826, 827, 827, 828, + 828, 829, 829, 830, 830, 831, 831, 832, 832, 833, 833, + 834, 834, 835, 835, 836, 836, 836, 837, 837, 838, 838, + 839, 839, 840, 840, 841, 841, 842, 842, 843, 843, 844, + 844, 845, 845, 845, 846, 846, 847, 847, 848, 848, 849, + 849, 850, 850, 851, 851, 852, 852, 852, 853, 853, 854, + 854, 855, 855, 856, 856, 857, 857, 857, 858, 858, 859, + 859, 860, 860, 861, 861, 862, 862, 862, 863, 863, 864, + 864, 865, 865, 866, 866, 866, 867, 867, 868, 868, 869, + 869, 869, 870, 870, 871, 871, 872, 872, 873, 873, 873, + 874, 874, 875, 875, 876, 876, 876, 877, 877, 878, 878, + 879, 879, 879, 880, 880, 881, 881, 882, 882, 882, 883, + 883, 884, 884, 885, 885, 885, 886, 886, 887, 887, 887, + 888, 888, 889, 889, 890, 890, 890, 891, 891, 892, 892, + 892, 893, 893, 894, 894, 895, 895, 895, 896, 896, 897, + 897, 897, 898, 898, 899, 899, 899, 900, 900, 901, 901, + 901, 902, 902, 903, 903, 903, 904, 904, 905, 905, 905, + 906, 906, 907, 907, 907, 908, 908, 909, 909, 909, 910, + 910, 911, 911, 911, 912, 912, 913, 913, 913, 914, 914, + 915, 915, 915, 916, 916, 916, 917, 917, 918, 918, 918, + 919, 919, 920, 920, 920, 921, 921, 921, 922, 922, 923, + 923, 923, 924, 924, 925, 925, 925, 926, 926, 926, 927, + 927, 928, 928, 928, 929, 929, 929, 930, 930, 931, 931, + 931, 932, 932, 932, 933, 933, 934, 934, 934, 935, 935, + 935, 936, 936, 936, 937, 937, 938, 938, 938, 939, 939, + 939, 940, 940, 941, 941, 941, 942, 942, 942, 943, 943, + 943, 944, 944, 945, 945, 945, 946, 946, 946, 947, 947, + 947, 948, 948, 948, 949, 949, 950, 950, 950, 951, 951, + 951, 952, 952, 952, 953, 953, 953, 954, 954, 955, 955, + 955, 956, 956, 956, 957, 957, 957, 958, 958, 958, 959, + 959, 959, 960, 960, 960, 961, 961, 962, 962, 962, 963, + 963, 963, 964, 964, 964, 965, 965, 965, 966, 966, 966, + 967, 967, 967, 968, 968, 968, 969, 969, 969, 970, 970, + 970, 971, 971, 971, 972, 972, 972, 973, 973, 973, 974, + 974, 974, 975, 975, 975, 976, 976, 976, 977, 977, 977, + 978, 978, 978, 979, 979, 979, 980, 980, 980, 981, 981, + 981, 982, 982, 982, 983, 983, 983, 984, 984, 984, 985, + 985, 985, 986, 986, 986, 987, 987, 987, 988, 988, 988, + 989, 989, 989, 990, 990, 990, 991, 991, 991, 992, 992, + 992, 993, 993, 993, 994, 994, 994, 994, 995, 995, 995, + 996, 996, 996, 997, 997, 997, 998, 998, 998, 999, 999, + 999, 1000, 1000, 1000, 1001, 1001, 1001, 1001, 1002, 1002, 1002, + 1003, 1003, 1003, 1004, 1004, 1004, 1005, 1005, 1005, 1006, 1006, + 1006, 1006, 1007, 1007, 1007, 1008, 1008, 1008, 1009, 1009, 1009, + 1010, 1010, 1010, 1011, 1011, 1011, 1011, 1012, 1012, 1012, 1013, + 1013, 1013, 1014, 1014, 1014, 1015, 1015, 1015, 1015, 1016, 1016, + 1016, 1017, 1017, 1017, 1018, 1018, 1018, 1018, 1019, 1019, 1019, + 1020, 1020, 1020, 1021, 1021, 1021, 1021, 1022, 1022, 1022, 1023, + 1023, +}; + +static const u16 xgamma10_04[GAMMA10_TABLE_LENGTH] = { + 0, 64, 84, 99, 111, 122, 131, 139, 147, 154, 161, + 167, 173, 178, 184, 189, 194, 199, 203, 208, 212, 216, + 220, 224, 228, 232, 235, 239, 243, 246, 249, 253, 256, + 259, 262, 265, 268, 271, 274, 277, 280, 283, 285, 288, + 291, 293, 296, 298, 301, 303, 306, 308, 311, 313, 315, + 318, 320, 322, 325, 327, 329, 331, 333, 335, 338, 340, + 342, 344, 346, 348, 350, 352, 354, 356, 358, 360, 362, + 364, 365, 367, 369, 371, 373, 375, 376, 378, 380, 382, + 383, 385, 387, 389, 390, 392, 394, 395, 397, 399, 400, + 402, 404, 405, 407, 408, 410, 412, 413, 415, 416, 418, + 419, 421, 422, 424, 425, 427, 428, 430, 431, 433, 434, + 436, 437, 438, 440, 441, 443, 444, 445, 447, 448, 450, + 451, 452, 454, 455, 456, 458, 459, 460, 462, 463, 464, + 466, 467, 468, 470, 471, 472, 473, 475, 476, 477, 478, + 480, 481, 482, 483, 485, 486, 487, 488, 489, 491, 492, + 493, 494, 495, 497, 498, 499, 500, 501, 503, 504, 505, + 506, 507, 508, 509, 511, 512, 513, 514, 515, 516, 517, + 518, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, + 530, 531, 533, 534, 535, 536, 537, 538, 539, 540, 541, + 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, + 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, + 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, + 575, 576, 577, 578, 578, 579, 580, 581, 582, 583, 584, + 585, 586, 587, 588, 589, 590, 591, 591, 592, 593, 594, + 595, 596, 597, 598, 599, 600, 600, 601, 602, 603, 604, + 605, 606, 607, 607, 608, 609, 610, 611, 612, 613, 614, + 614, 615, 616, 617, 618, 619, 620, 620, 621, 622, 623, + 624, 625, 625, 626, 627, 628, 629, 630, 630, 631, 632, + 633, 634, 635, 635, 636, 637, 638, 639, 639, 640, 641, + 642, 643, 643, 644, 645, 646, 647, 647, 648, 649, 650, + 651, 651, 652, 653, 654, 655, 655, 656, 657, 658, 658, + 659, 660, 661, 662, 662, 663, 664, 665, 665, 666, 667, + 668, 668, 669, 670, 671, 671, 672, 673, 674, 674, 675, + 676, 677, 677, 678, 679, 680, 680, 681, 682, 683, 683, + 684, 685, 685, 686, 687, 688, 688, 689, 690, 691, 691, + 692, 693, 693, 694, 695, 696, 696, 697, 698, 698, 699, + 700, 701, 701, 702, 703, 703, 704, 705, 705, 706, 707, + 708, 708, 709, 710, 710, 711, 712, 712, 713, 714, 714, + 715, 716, 717, 717, 718, 719, 719, 720, 721, 721, 722, + 723, 723, 724, 725, 725, 726, 727, 727, 728, 729, 729, + 730, 731, 731, 732, 733, 733, 734, 735, 735, 736, 737, + 737, 738, 739, 739, 740, 740, 741, 742, 742, 743, 744, + 744, 745, 746, 746, 747, 748, 748, 749, 749, 750, 751, + 751, 752, 753, 753, 754, 755, 755, 756, 756, 757, 758, + 758, 759, 760, 760, 761, 761, 762, 763, 763, 764, 765, + 765, 766, 766, 767, 768, 768, 769, 769, 770, 771, 771, + 772, 773, 773, 774, 774, 775, 776, 776, 777, 777, 778, + 779, 779, 780, 780, 781, 782, 782, 783, 783, 784, 785, + 785, 786, 786, 787, 788, 788, 789, 789, 790, 791, 791, + 792, 792, 793, 793, 794, 795, 795, 796, 796, 797, 798, + 798, 799, 799, 800, 800, 801, 802, 802, 803, 803, 804, + 804, 805, 806, 806, 807, 807, 808, 808, 809, 810, 810, + 811, 811, 812, 812, 813, 814, 814, 815, 815, 816, 816, + 817, 818, 818, 819, 819, 820, 820, 821, 821, 822, 823, + 823, 824, 824, 825, 825, 826, 826, 827, 827, 828, 829, + 829, 830, 830, 831, 831, 832, 832, 833, 834, 834, 835, + 835, 836, 836, 837, 837, 838, 838, 839, 839, 840, 841, + 841, 842, 842, 843, 843, 844, 844, 845, 845, 846, 846, + 847, 847, 848, 849, 849, 850, 850, 851, 851, 852, 852, + 853, 853, 854, 854, 855, 855, 856, 856, 857, 857, 858, + 859, 859, 860, 860, 861, 861, 862, 862, 863, 863, 864, + 864, 865, 865, 866, 866, 867, 867, 868, 868, 869, 869, + 870, 870, 871, 871, 872, 872, 873, 873, 874, 874, 875, + 875, 876, 876, 877, 877, 878, 878, 879, 879, 880, 880, + 881, 881, 882, 882, 883, 883, 884, 884, 885, 885, 886, + 886, 887, 887, 888, 888, 889, 889, 890, 890, 891, 891, + 892, 892, 893, 893, 894, 894, 895, 895, 896, 896, 897, + 897, 898, 898, 899, 899, 900, 900, 901, 901, 902, 902, + 903, 903, 904, 904, 905, 905, 905, 906, 906, 907, 907, + 908, 908, 909, 909, 910, 910, 911, 911, 912, 912, 913, + 913, 914, 914, 915, 915, 915, 916, 916, 917, 917, 918, + 918, 919, 919, 920, 920, 921, 921, 922, 922, 923, 923, + 923, 924, 924, 925, 925, 926, 926, 927, 927, 928, 928, + 929, 929, 929, 930, 930, 931, 931, 932, 932, 933, 933, + 934, 934, 935, 935, 935, 936, 936, 937, 937, 938, 938, + 939, 939, 940, 940, 940, 941, 941, 942, 942, 943, 943, + 944, 944, 945, 945, 945, 946, 946, 947, 947, 948, 948, + 949, 949, 949, 950, 950, 951, 951, 952, 952, 953, 953, + 953, 954, 954, 955, 955, 956, 956, 957, 957, 957, 958, + 958, 959, 959, 960, 960, 961, 961, 961, 962, 962, 963, + 963, 964, 964, 965, 965, 965, 966, 966, 967, 967, 968, + 968, 968, 969, 969, 970, 970, 971, 971, 971, 972, 972, + 973, 973, 974, 974, 974, 975, 975, 976, 976, 977, 977, + 977, 978, 978, 979, 979, 980, 980, 980, 981, 981, 982, + 982, 983, 983, 983, 984, 984, 985, 985, 986, 986, 986, + 987, 987, 988, 988, 989, 989, 989, 990, 990, 991, 991, + 991, 992, 992, 993, 993, 994, 994, 994, 995, 995, 996, + 996, 996, 997, 997, 998, 998, 999, 999, 999, 1000, 1000, + 1001, 1001, 1001, 1002, 1002, 1003, 1003, 1004, 1004, 1004, 1005, + 1005, 1006, 1006, 1006, 1007, 1007, 1008, 1008, 1008, 1009, 1009, + 1010, 1010, 1010, 1011, 1011, 1012, 1012, 1013, 1013, 1013, 1014, + 1014, 1015, 1015, 1015, 1016, 1016, 1017, 1017, 1017, 1018, 1018, + 1019, 1019, 1019, 1020, 1020, 1021, 1021, 1021, 1022, 1022, 1023, + 1023, +}; + +static const u16 xgamma10_05[GAMMA10_TABLE_LENGTH] = { + 0, 32, 45, 55, 64, 72, 78, 85, 90, 96, 101, + 106, 111, 115, 120, 124, 128, 132, 136, 139, 143, 147, + 150, 153, 157, 160, 163, 166, 169, 172, 175, 178, 181, + 184, 186, 189, 192, 195, 197, 200, 202, 205, 207, 210, + 212, 215, 217, 219, 222, 224, 226, 228, 231, 233, 235, + 237, 239, 241, 244, 246, 248, 250, 252, 254, 256, 258, + 260, 262, 264, 266, 268, 270, 271, 273, 275, 277, 279, + 281, 282, 284, 286, 288, 290, 291, 293, 295, 297, 298, + 300, 302, 303, 305, 307, 308, 310, 312, 313, 315, 317, + 318, 320, 321, 323, 325, 326, 328, 329, 331, 332, 334, + 335, 337, 338, 340, 341, 343, 344, 346, 347, 349, 350, + 352, 353, 355, 356, 358, 359, 360, 362, 363, 365, 366, + 367, 369, 370, 372, 373, 374, 376, 377, 378, 380, 381, + 382, 384, 385, 386, 388, 389, 390, 392, 393, 394, 396, + 397, 398, 399, 401, 402, 403, 405, 406, 407, 408, 410, + 411, 412, 413, 415, 416, 417, 418, 419, 421, 422, 423, + 424, 426, 427, 428, 429, 430, 431, 433, 434, 435, 436, + 437, 439, 440, 441, 442, 443, 444, 445, 447, 448, 449, + 450, 451, 452, 453, 455, 456, 457, 458, 459, 460, 461, + 462, 463, 465, 466, 467, 468, 469, 470, 471, 472, 473, + 474, 475, 477, 478, 479, 480, 481, 482, 483, 484, 485, + 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 497, + 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, + 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, + 520, 521, 522, 523, 524, 525, 526, 527, 527, 528, 529, + 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, + 541, 542, 543, 544, 545, 546, 547, 547, 548, 549, 550, + 551, 552, 553, 554, 555, 556, 557, 558, 559, 559, 560, + 561, 562, 563, 564, 565, 566, 567, 568, 569, 569, 570, + 571, 572, 573, 574, 575, 576, 577, 577, 578, 579, 580, + 581, 582, 583, 584, 585, 585, 586, 587, 588, 589, 590, + 591, 591, 592, 593, 594, 595, 596, 597, 598, 598, 599, + 600, 601, 602, 603, 603, 604, 605, 606, 607, 608, 609, + 609, 610, 611, 612, 613, 614, 614, 615, 616, 617, 618, + 619, 619, 620, 621, 622, 623, 623, 624, 625, 626, 627, + 628, 628, 629, 630, 631, 632, 632, 633, 634, 635, 636, + 636, 637, 638, 639, 640, 640, 641, 642, 643, 644, 644, + 645, 646, 647, 648, 648, 649, 650, 651, 652, 652, 653, + 654, 655, 655, 656, 657, 658, 659, 659, 660, 661, 662, + 662, 663, 664, 665, 666, 666, 667, 668, 669, 669, 670, + 671, 672, 672, 673, 674, 675, 675, 676, 677, 678, 678, + 679, 680, 681, 681, 682, 683, 684, 684, 685, 686, 687, + 687, 688, 689, 690, 690, 691, 692, 693, 693, 694, 695, + 696, 696, 697, 698, 699, 699, 700, 701, 701, 702, 703, + 704, 704, 705, 706, 707, 707, 708, 709, 709, 710, 711, + 712, 712, 713, 714, 714, 715, 716, 717, 717, 718, 719, + 719, 720, 721, 722, 722, 723, 724, 724, 725, 726, 727, + 727, 728, 729, 729, 730, 731, 731, 732, 733, 734, 734, + 735, 736, 736, 737, 738, 738, 739, 740, 740, 741, 742, + 743, 743, 744, 745, 745, 746, 747, 747, 748, 749, 749, + 750, 751, 751, 752, 753, 754, 754, 755, 756, 756, 757, + 758, 758, 759, 760, 760, 761, 762, 762, 763, 764, 764, + 765, 766, 766, 767, 768, 768, 769, 770, 770, 771, 772, + 772, 773, 774, 774, 775, 776, 776, 777, 778, 778, 779, + 780, 780, 781, 781, 782, 783, 783, 784, 785, 785, 786, + 787, 787, 788, 789, 789, 790, 791, 791, 792, 793, 793, + 794, 794, 795, 796, 796, 797, 798, 798, 799, 800, 800, + 801, 802, 802, 803, 803, 804, 805, 805, 806, 807, 807, + 808, 809, 809, 810, 810, 811, 812, 812, 813, 814, 814, + 815, 815, 816, 817, 817, 818, 819, 819, 820, 820, 821, + 822, 822, 823, 824, 824, 825, 825, 826, 827, 827, 828, + 829, 829, 830, 830, 831, 832, 832, 833, 833, 834, 835, + 835, 836, 836, 837, 838, 838, 839, 840, 840, 841, 841, + 842, 843, 843, 844, 844, 845, 846, 846, 847, 847, 848, + 849, 849, 850, 850, 851, 852, 852, 853, 853, 854, 855, + 855, 856, 856, 857, 858, 858, 859, 859, 860, 861, 861, + 862, 862, 863, 864, 864, 865, 865, 866, 867, 867, 868, + 868, 869, 869, 870, 871, 871, 872, 872, 873, 874, 874, + 875, 875, 876, 877, 877, 878, 878, 879, 879, 880, 881, + 881, 882, 882, 883, 883, 884, 885, 885, 886, 886, 887, + 888, 888, 889, 889, 890, 890, 891, 892, 892, 893, 893, + 894, 894, 895, 896, 896, 897, 897, 898, 898, 899, 900, + 900, 901, 901, 902, 902, 903, 904, 904, 905, 905, 906, + 906, 907, 907, 908, 909, 909, 910, 910, 911, 911, 912, + 913, 913, 914, 914, 915, 915, 916, 916, 917, 918, 918, + 919, 919, 920, 920, 921, 921, 922, 923, 923, 924, 924, + 925, 925, 926, 926, 927, 928, 928, 929, 929, 930, 930, + 931, 931, 932, 932, 933, 934, 934, 935, 935, 936, 936, + 937, 937, 938, 939, 939, 940, 940, 941, 941, 942, 942, + 943, 943, 944, 944, 945, 946, 946, 947, 947, 948, 948, + 949, 949, 950, 950, 951, 952, 952, 953, 953, 954, 954, + 955, 955, 956, 956, 957, 957, 958, 958, 959, 960, 960, + 961, 961, 962, 962, 963, 963, 964, 964, 965, 965, 966, + 966, 967, 967, 968, 969, 969, 970, 970, 971, 971, 972, + 972, 973, 973, 974, 974, 975, 975, 976, 976, 977, 977, + 978, 979, 979, 980, 980, 981, 981, 982, 982, 983, 983, + 984, 984, 985, 985, 986, 986, 987, 987, 988, 988, 989, + 989, 990, 990, 991, 992, 992, 993, 993, 994, 994, 995, + 995, 996, 996, 997, 997, 998, 998, 999, 999, 1000, 1000, + 1001, 1001, 1002, 1002, 1003, 1003, 1004, 1004, 1005, 1005, 1006, + 1006, 1007, 1007, 1008, 1008, 1009, 1009, 1010, 1010, 1011, 1011, + 1012, 1012, 1013, 1013, 1014, 1014, 1015, 1015, 1016, 1016, 1017, + 1017, 1018, 1018, 1019, 1019, 1020, 1020, 1021, 1021, 1022, 1022, + 1023, +}; + +static const u16 xgamma10_06[GAMMA10_TABLE_LENGTH] = { + 0, 16, 24, 31, 37, 42, 47, 51, 56, 60, 64, + 67, 71, 75, 78, 81, 84, 88, 91, 94, 97, 99, + 102, 105, 108, 110, 113, 116, 118, 121, 123, 126, 128, + 130, 133, 135, 137, 140, 142, 144, 146, 148, 151, 153, + 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, + 177, 179, 181, 183, 185, 187, 188, 190, 192, 194, 196, + 198, 199, 201, 203, 205, 206, 208, 210, 212, 213, 215, + 217, 218, 220, 222, 223, 225, 227, 228, 230, 232, 233, + 235, 236, 238, 240, 241, 243, 244, 246, 247, 249, 250, + 252, 253, 255, 257, 258, 260, 261, 263, 264, 265, 267, + 268, 270, 271, 273, 274, 276, 277, 279, 280, 281, 283, + 284, 286, 287, 288, 290, 291, 293, 294, 295, 297, 298, + 299, 301, 302, 303, 305, 306, 308, 309, 310, 312, 313, + 314, 315, 317, 318, 319, 321, 322, 323, 325, 326, 327, + 328, 330, 331, 332, 334, 335, 336, 337, 339, 340, 341, + 342, 344, 345, 346, 347, 349, 350, 351, 352, 353, 355, + 356, 357, 358, 359, 361, 362, 363, 364, 365, 367, 368, + 369, 370, 371, 373, 374, 375, 376, 377, 378, 380, 381, + 382, 383, 384, 385, 387, 388, 389, 390, 391, 392, 393, + 394, 396, 397, 398, 399, 400, 401, 402, 403, 405, 406, + 407, 408, 409, 410, 411, 412, 413, 415, 416, 417, 418, + 419, 420, 421, 422, 423, 424, 425, 426, 428, 429, 430, + 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, + 442, 443, 445, 446, 447, 448, 449, 450, 451, 452, 453, + 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, + 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, + 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, + 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, + 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 507, + 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, + 519, 520, 521, 522, 523, 524, 525, 525, 526, 527, 528, + 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 538, + 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 548, + 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 558, + 559, 560, 561, 562, 563, 564, 565, 566, 566, 567, 568, + 569, 570, 571, 572, 573, 574, 574, 575, 576, 577, 578, + 579, 580, 581, 581, 582, 583, 584, 585, 586, 587, 588, + 588, 589, 590, 591, 592, 593, 594, 594, 595, 596, 597, + 598, 599, 600, 601, 601, 602, 603, 604, 605, 606, 606, + 607, 608, 609, 610, 611, 612, 612, 613, 614, 615, 616, + 617, 617, 618, 619, 620, 621, 622, 622, 623, 624, 625, + 626, 627, 627, 628, 629, 630, 631, 632, 632, 633, 634, + 635, 636, 637, 637, 638, 639, 640, 641, 642, 642, 643, + 644, 645, 646, 646, 647, 648, 649, 650, 650, 651, 652, + 653, 654, 655, 655, 656, 657, 658, 659, 659, 660, 661, + 662, 663, 663, 664, 665, 666, 667, 667, 668, 669, 670, + 671, 671, 672, 673, 674, 675, 675, 676, 677, 678, 678, + 679, 680, 681, 682, 682, 683, 684, 685, 686, 686, 687, + 688, 689, 689, 690, 691, 692, 693, 693, 694, 695, 696, + 696, 697, 698, 699, 700, 700, 701, 702, 703, 703, 704, + 705, 706, 707, 707, 708, 709, 710, 710, 711, 712, 713, + 713, 714, 715, 716, 716, 717, 718, 719, 719, 720, 721, + 722, 723, 723, 724, 725, 726, 726, 727, 728, 729, 729, + 730, 731, 732, 732, 733, 734, 735, 735, 736, 737, 738, + 738, 739, 740, 741, 741, 742, 743, 743, 744, 745, 746, + 746, 747, 748, 749, 749, 750, 751, 752, 752, 753, 754, + 755, 755, 756, 757, 758, 758, 759, 760, 760, 761, 762, + 763, 763, 764, 765, 766, 766, 767, 768, 768, 769, 770, + 771, 771, 772, 773, 774, 774, 775, 776, 776, 777, 778, + 779, 779, 780, 781, 781, 782, 783, 784, 784, 785, 786, + 786, 787, 788, 789, 789, 790, 791, 791, 792, 793, 794, + 794, 795, 796, 796, 797, 798, 799, 799, 800, 801, 801, + 802, 803, 803, 804, 805, 806, 806, 807, 808, 808, 809, + 810, 811, 811, 812, 813, 813, 814, 815, 815, 816, 817, + 818, 818, 819, 820, 820, 821, 822, 822, 823, 824, 824, + 825, 826, 827, 827, 828, 829, 829, 830, 831, 831, 832, + 833, 833, 834, 835, 835, 836, 837, 838, 838, 839, 840, + 840, 841, 842, 842, 843, 844, 844, 845, 846, 846, 847, + 848, 848, 849, 850, 851, 851, 852, 853, 853, 854, 855, + 855, 856, 857, 857, 858, 859, 859, 860, 861, 861, 862, + 863, 863, 864, 865, 865, 866, 867, 867, 868, 869, 869, + 870, 871, 871, 872, 873, 873, 874, 875, 875, 876, 877, + 877, 878, 879, 879, 880, 881, 881, 882, 883, 883, 884, + 885, 885, 886, 887, 887, 888, 889, 889, 890, 891, 891, + 892, 893, 893, 894, 895, 895, 896, 897, 897, 898, 898, + 899, 900, 900, 901, 902, 902, 903, 904, 904, 905, 906, + 906, 907, 908, 908, 909, 910, 910, 911, 911, 912, 913, + 913, 914, 915, 915, 916, 917, 917, 918, 919, 919, 920, + 921, 921, 922, 922, 923, 924, 924, 925, 926, 926, 927, + 928, 928, 929, 930, 930, 931, 931, 932, 933, 933, 934, + 935, 935, 936, 937, 937, 938, 938, 939, 940, 940, 941, + 942, 942, 943, 944, 944, 945, 945, 946, 947, 947, 948, + 949, 949, 950, 950, 951, 952, 952, 953, 954, 954, 955, + 956, 956, 957, 957, 958, 959, 959, 960, 961, 961, 962, + 962, 963, 964, 964, 965, 966, 966, 967, 967, 968, 969, + 969, 970, 970, 971, 972, 972, 973, 974, 974, 975, 975, + 976, 977, 977, 978, 979, 979, 980, 980, 981, 982, 982, + 983, 983, 984, 985, 985, 986, 987, 987, 988, 988, 989, + 990, 990, 991, 991, 992, 993, 993, 994, 995, 995, 996, + 996, 997, 998, 998, 999, 999, 1000, 1001, 1001, 1002, 1002, + 1003, 1004, 1004, 1005, 1006, 1006, 1007, 1007, 1008, 1009, 1009, + 1010, 1010, 1011, 1012, 1012, 1013, 1013, 1014, 1015, 1015, 1016, + 1016, 1017, 1018, 1018, 1019, 1019, 1020, 1021, 1021, 1022, 1022, + 1023, +}; + +static const u16 xgamma10_07[GAMMA10_TABLE_LENGTH] = { + 0, 8, 13, 17, 21, 25, 28, 31, 34, 37, 40, + 43, 46, 48, 51, 53, 56, 58, 60, 63, 65, 67, + 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, + 92, 94, 96, 98, 100, 102, 104, 106, 108, 109, 111, + 113, 115, 117, 118, 120, 122, 124, 125, 127, 129, 131, + 132, 134, 136, 137, 139, 140, 142, 144, 145, 147, 149, + 150, 152, 153, 155, 157, 158, 160, 161, 163, 164, 166, + 167, 169, 170, 172, 173, 175, 176, 178, 179, 181, 182, + 184, 185, 187, 188, 190, 191, 192, 194, 195, 197, 198, + 199, 201, 202, 204, 205, 206, 208, 209, 211, 212, 213, + 215, 216, 217, 219, 220, 222, 223, 224, 226, 227, 228, + 230, 231, 232, 234, 235, 236, 237, 239, 240, 241, 243, + 244, 245, 247, 248, 249, 250, 252, 253, 254, 256, 257, + 258, 259, 261, 262, 263, 264, 266, 267, 268, 269, 271, + 272, 273, 274, 275, 277, 278, 279, 280, 282, 283, 284, + 285, 286, 288, 289, 290, 291, 292, 294, 295, 296, 297, + 298, 300, 301, 302, 303, 304, 306, 307, 308, 309, 310, + 311, 313, 314, 315, 316, 317, 318, 319, 321, 322, 323, + 324, 325, 326, 327, 329, 330, 331, 332, 333, 334, 335, + 337, 338, 339, 340, 341, 342, 343, 344, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 357, 358, 359, 360, + 361, 362, 363, 364, 365, 366, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378, 379, 380, 382, 383, 384, + 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, + 396, 397, 398, 400, 401, 402, 403, 404, 405, 406, 407, + 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, + 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, + 430, 431, 432, 433, 434, 435, 436, 437, 439, 440, 441, + 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, + 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, + 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, + 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, + 485, 486, 487, 488, 489, 490, 491, 492, 492, 493, 494, + 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, + 506, 507, 508, 509, 510, 511, 511, 512, 513, 514, 515, + 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, + 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, + 537, 538, 538, 539, 540, 541, 542, 543, 544, 545, 546, + 547, 548, 549, 549, 550, 551, 552, 553, 554, 555, 556, + 557, 558, 559, 560, 560, 561, 562, 563, 564, 565, 566, + 567, 568, 569, 569, 570, 571, 572, 573, 574, 575, 576, + 577, 578, 578, 579, 580, 581, 582, 583, 584, 585, 586, + 586, 587, 588, 589, 590, 591, 592, 593, 594, 594, 595, + 596, 597, 598, 599, 600, 601, 601, 602, 603, 604, 605, + 606, 607, 608, 608, 609, 610, 611, 612, 613, 614, 615, + 615, 616, 617, 618, 619, 620, 621, 622, 622, 623, 624, + 625, 626, 627, 628, 628, 629, 630, 631, 632, 633, 634, + 634, 635, 636, 637, 638, 639, 640, 640, 641, 642, 643, + 644, 645, 646, 646, 647, 648, 649, 650, 651, 652, 652, + 653, 654, 655, 656, 657, 657, 658, 659, 660, 661, 662, + 663, 663, 664, 665, 666, 667, 668, 668, 669, 670, 671, + 672, 673, 673, 674, 675, 676, 677, 678, 678, 679, 680, + 681, 682, 683, 683, 684, 685, 686, 687, 688, 688, 689, + 690, 691, 692, 693, 693, 694, 695, 696, 697, 698, 698, + 699, 700, 701, 702, 703, 703, 704, 705, 706, 707, 707, + 708, 709, 710, 711, 712, 712, 713, 714, 715, 716, 716, + 717, 718, 719, 720, 721, 721, 722, 723, 724, 725, 725, + 726, 727, 728, 729, 729, 730, 731, 732, 733, 733, 734, + 735, 736, 737, 738, 738, 739, 740, 741, 742, 742, 743, + 744, 745, 746, 746, 747, 748, 749, 750, 750, 751, 752, + 753, 754, 754, 755, 756, 757, 758, 758, 759, 760, 761, + 761, 762, 763, 764, 765, 765, 766, 767, 768, 769, 769, + 770, 771, 772, 773, 773, 774, 775, 776, 777, 777, 778, + 779, 780, 780, 781, 782, 783, 784, 784, 785, 786, 787, + 788, 788, 789, 790, 791, 791, 792, 793, 794, 795, 795, + 796, 797, 798, 798, 799, 800, 801, 802, 802, 803, 804, + 805, 805, 806, 807, 808, 809, 809, 810, 811, 812, 812, + 813, 814, 815, 816, 816, 817, 818, 819, 819, 820, 821, + 822, 822, 823, 824, 825, 826, 826, 827, 828, 829, 829, + 830, 831, 832, 832, 833, 834, 835, 835, 836, 837, 838, + 839, 839, 840, 841, 842, 842, 843, 844, 845, 845, 846, + 847, 848, 848, 849, 850, 851, 851, 852, 853, 854, 854, + 855, 856, 857, 857, 858, 859, 860, 860, 861, 862, 863, + 864, 864, 865, 866, 867, 867, 868, 869, 870, 870, 871, + 872, 873, 873, 874, 875, 876, 876, 877, 878, 879, 879, + 880, 881, 881, 882, 883, 884, 884, 885, 886, 887, 887, + 888, 889, 890, 890, 891, 892, 893, 893, 894, 895, 896, + 896, 897, 898, 899, 899, 900, 901, 902, 902, 903, 904, + 904, 905, 906, 907, 907, 908, 909, 910, 910, 911, 912, + 913, 913, 914, 915, 916, 916, 917, 918, 918, 919, 920, + 921, 921, 922, 923, 924, 924, 925, 926, 927, 927, 928, + 929, 929, 930, 931, 932, 932, 933, 934, 935, 935, 936, + 937, 937, 938, 939, 940, 940, 941, 942, 943, 943, 944, + 945, 945, 946, 947, 948, 948, 949, 950, 950, 951, 952, + 953, 953, 954, 955, 956, 956, 957, 958, 958, 959, 960, + 961, 961, 962, 963, 963, 964, 965, 966, 966, 967, 968, + 968, 969, 970, 971, 971, 972, 973, 973, 974, 975, 976, + 976, 977, 978, 978, 979, 980, 981, 981, 982, 983, 983, + 984, 985, 986, 986, 987, 988, 988, 989, 990, 991, 991, + 992, 993, 993, 994, 995, 996, 996, 997, 998, 998, 999, + 1000, 1000, 1001, 1002, 1003, 1003, 1004, 1005, 1005, 1006, 1007, + 1008, 1008, 1009, 1010, 1010, 1011, 1012, 1012, 1013, 1014, 1015, + 1015, 1016, 1017, 1017, 1018, 1019, 1019, 1020, 1021, 1022, 1022, + 1023, +}; + +static const u16 xgamma10_08[GAMMA10_TABLE_LENGTH] = { + 0, 4, 7, 10, 12, 14, 17, 19, 21, 23, 25, + 27, 29, 31, 33, 35, 37, 39, 40, 42, 44, 46, + 47, 49, 51, 53, 54, 56, 58, 59, 61, 62, 64, + 66, 67, 69, 70, 72, 73, 75, 76, 78, 80, 81, + 83, 84, 86, 87, 89, 90, 91, 93, 94, 96, 97, + 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 113, + 114, 116, 117, 118, 120, 121, 122, 124, 125, 126, 128, + 129, 131, 132, 133, 135, 136, 137, 138, 140, 141, 142, + 144, 145, 146, 148, 149, 150, 152, 153, 154, 155, 157, + 158, 159, 160, 162, 163, 164, 166, 167, 168, 169, 171, + 172, 173, 174, 176, 177, 178, 179, 181, 182, 183, 184, + 185, 187, 188, 189, 190, 192, 193, 194, 195, 196, 198, + 199, 200, 201, 202, 204, 205, 206, 207, 208, 210, 211, + 212, 213, 214, 216, 217, 218, 219, 220, 221, 223, 224, + 225, 226, 227, 228, 230, 231, 232, 233, 234, 235, 237, + 238, 239, 240, 241, 242, 243, 245, 246, 247, 248, 249, + 250, 251, 253, 254, 255, 256, 257, 258, 259, 260, 262, + 263, 264, 265, 266, 267, 268, 269, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 304, 305, 306, 307, 308, 309, 310, + 311, 312, 313, 314, 315, 316, 318, 319, 320, 321, 322, + 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, + 346, 347, 348, 349, 350, 351, 352, 353, 355, 356, 357, + 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, + 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, + 380, 381, 382, 383, 384, 385, 386, 388, 389, 390, 391, + 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, + 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, + 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, + 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, + 447, 448, 449, 450, 451, 451, 452, 453, 454, 455, 456, + 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, + 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, + 479, 480, 481, 482, 483, 484, 485, 486, 486, 487, 488, + 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, + 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 509, + 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, + 521, 522, 523, 524, 525, 526, 527, 527, 528, 529, 530, + 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, + 542, 543, 543, 544, 545, 546, 547, 548, 549, 550, 551, + 552, 553, 554, 555, 556, 557, 557, 558, 559, 560, 561, + 562, 563, 564, 565, 566, 567, 568, 569, 570, 570, 571, + 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, + 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, + 593, 594, 594, 595, 596, 597, 598, 599, 600, 601, 602, + 603, 604, 604, 605, 606, 607, 608, 609, 610, 611, 612, + 613, 614, 615, 615, 616, 617, 618, 619, 620, 621, 622, + 623, 624, 624, 625, 626, 627, 628, 629, 630, 631, 632, + 633, 634, 634, 635, 636, 637, 638, 639, 640, 641, 642, + 643, 643, 644, 645, 646, 647, 648, 649, 650, 651, 651, + 652, 653, 654, 655, 656, 657, 658, 659, 660, 660, 661, + 662, 663, 664, 665, 666, 667, 668, 668, 669, 670, 671, + 672, 673, 674, 675, 676, 676, 677, 678, 679, 680, 681, + 682, 683, 684, 684, 685, 686, 687, 688, 689, 690, 691, + 691, 692, 693, 694, 695, 696, 697, 698, 699, 699, 700, + 701, 702, 703, 704, 705, 706, 706, 707, 708, 709, 710, + 711, 712, 713, 713, 714, 715, 716, 717, 718, 719, 720, + 720, 721, 722, 723, 724, 725, 726, 727, 727, 728, 729, + 730, 731, 732, 733, 734, 734, 735, 736, 737, 738, 739, + 740, 740, 741, 742, 743, 744, 745, 746, 747, 747, 748, + 749, 750, 751, 752, 753, 753, 754, 755, 756, 757, 758, + 759, 759, 760, 761, 762, 763, 764, 765, 766, 766, 767, + 768, 769, 770, 771, 772, 772, 773, 774, 775, 776, 777, + 778, 778, 779, 780, 781, 782, 783, 784, 784, 785, 786, + 787, 788, 789, 790, 790, 791, 792, 793, 794, 795, 795, + 796, 797, 798, 799, 800, 801, 801, 802, 803, 804, 805, + 806, 807, 807, 808, 809, 810, 811, 812, 812, 813, 814, + 815, 816, 817, 818, 818, 819, 820, 821, 822, 823, 823, + 824, 825, 826, 827, 828, 829, 829, 830, 831, 832, 833, + 834, 834, 835, 836, 837, 838, 839, 839, 840, 841, 842, + 843, 844, 845, 845, 846, 847, 848, 849, 850, 850, 851, + 852, 853, 854, 855, 855, 856, 857, 858, 859, 860, 860, + 861, 862, 863, 864, 865, 865, 866, 867, 868, 869, 870, + 870, 871, 872, 873, 874, 875, 875, 876, 877, 878, 879, + 880, 880, 881, 882, 883, 884, 885, 885, 886, 887, 888, + 889, 890, 890, 891, 892, 893, 894, 895, 895, 896, 897, + 898, 899, 899, 900, 901, 902, 903, 904, 904, 905, 906, + 907, 908, 909, 909, 910, 911, 912, 913, 913, 914, 915, + 916, 917, 918, 918, 919, 920, 921, 922, 923, 923, 924, + 925, 926, 927, 927, 928, 929, 930, 931, 932, 932, 933, + 934, 935, 936, 936, 937, 938, 939, 940, 941, 941, 942, + 943, 944, 945, 945, 946, 947, 948, 949, 950, 950, 951, + 952, 953, 954, 954, 955, 956, 957, 958, 958, 959, 960, + 961, 962, 963, 963, 964, 965, 966, 967, 967, 968, 969, + 970, 971, 971, 972, 973, 974, 975, 976, 976, 977, 978, + 979, 980, 980, 981, 982, 983, 984, 984, 985, 986, 987, + 988, 988, 989, 990, 991, 992, 992, 993, 994, 995, 996, + 997, 997, 998, 999, 1000, 1001, 1001, 1002, 1003, 1004, 1005, + 1005, 1006, 1007, 1008, 1009, 1009, 1010, 1011, 1012, 1013, 1013, + 1014, 1015, 1016, 1017, 1017, 1018, 1019, 1020, 1021, 1021, 1022, + 1023, +}; + +static const u16 xgamma10_09[GAMMA10_TABLE_LENGTH] = { + 0, 2, 4, 5, 7, 9, 10, 12, 13, 14, 16, + 17, 19, 20, 22, 23, 24, 26, 27, 28, 30, 31, + 32, 34, 35, 36, 38, 39, 40, 41, 43, 44, 45, + 47, 48, 49, 50, 52, 53, 54, 55, 57, 58, 59, + 60, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, + 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 86, + 87, 88, 89, 90, 92, 93, 94, 95, 96, 97, 99, + 100, 101, 102, 103, 104, 106, 107, 108, 109, 110, 111, + 112, 114, 115, 116, 117, 118, 119, 120, 122, 123, 124, + 125, 126, 127, 128, 130, 131, 132, 133, 134, 135, 136, + 137, 139, 140, 141, 142, 143, 144, 145, 146, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, + 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, + 358, 359, 360, 361, 362, 363, 364, 365, 367, 368, 369, + 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, + 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, + 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, + 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, + 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 425, 426, 427, 427, 428, 429, 430, 431, 432, 433, 434, + 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, + 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, + 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, + 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, + 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, + 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, + 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, + 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, + 522, 523, 524, 525, 525, 526, 527, 528, 529, 530, 531, + 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, + 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, + 554, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, + 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, + 575, 576, 577, 578, 579, 579, 580, 581, 582, 583, 584, + 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, + 596, 597, 598, 599, 600, 601, 601, 602, 603, 604, 605, + 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, + 617, 618, 619, 620, 621, 621, 622, 623, 624, 625, 626, + 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, + 638, 639, 640, 640, 641, 642, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, + 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, + 669, 670, 671, 672, 673, 674, 675, 675, 676, 677, 678, + 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, + 690, 691, 691, 692, 693, 694, 695, 696, 697, 698, 699, + 700, 701, 702, 703, 704, 705, 706, 706, 707, 708, 709, + 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, + 721, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, + 731, 732, 733, 734, 735, 735, 736, 737, 738, 739, 740, + 741, 742, 743, 744, 745, 746, 747, 748, 749, 749, 750, + 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, + 762, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, + 772, 773, 774, 775, 776, 776, 777, 778, 779, 780, 781, + 782, 783, 784, 785, 786, 787, 788, 788, 789, 790, 791, + 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 801, + 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, + 813, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, + 823, 824, 825, 825, 826, 827, 828, 829, 830, 831, 832, + 833, 834, 835, 836, 836, 837, 838, 839, 840, 841, 842, + 843, 844, 845, 846, 847, 848, 848, 849, 850, 851, 852, + 853, 854, 855, 856, 857, 858, 859, 859, 860, 861, 862, + 863, 864, 865, 866, 867, 868, 869, 870, 870, 871, 872, + 873, 874, 875, 876, 877, 878, 879, 880, 881, 881, 882, + 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 892, + 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 902, + 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, + 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, + 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, + 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, + 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, + 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 962, + 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 972, + 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 982, + 983, 984, 985, 986, 987, 988, 989, 990, 991, 991, 992, + 993, 994, 995, 996, 997, 998, 999, 1000, 1000, 1001, 1002, + 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1009, 1010, 1011, 1012, + 1013, 1014, 1015, 1016, 1017, 1018, 1018, 1019, 1020, 1021, 1022, + 1023, +}; + +static const u16 xgamma10_10[GAMMA10_TABLE_LENGTH] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, + 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, + 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, + 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, + 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, + 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, + 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, + 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, + 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, + 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, + 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, + 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, + 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, + 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, + 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, + 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, + 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, + 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, + 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, + 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, + 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, + 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, + 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, + 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, + 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, + 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, + 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, + 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, + 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, + 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, + 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, + 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, + 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, + 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, + 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, + 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, + 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, + 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, + 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, + 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, + 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, + 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, + 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, + 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, + 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, + 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, + 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, + 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, + 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, + 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, + 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, + 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, + 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, + 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, + 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, + 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, + 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, + 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, + 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, + 1023, +}; + +static const u16 xgamma10_11[GAMMA10_TABLE_LENGTH] = { + 0, 1, 1, 2, 2, 3, 4, 4, 5, 6, 6, + 7, 8, 8, 9, 10, 11, 11, 12, 13, 13, 14, + 15, 16, 16, 17, 18, 19, 20, 20, 21, 22, 23, + 23, 24, 25, 26, 27, 27, 28, 29, 30, 31, 31, + 32, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, + 41, 42, 43, 44, 44, 45, 46, 47, 48, 49, 49, + 50, 51, 52, 53, 54, 54, 55, 56, 57, 58, 59, + 59, 60, 61, 62, 63, 64, 65, 65, 66, 67, 68, + 69, 70, 71, 71, 72, 73, 74, 75, 76, 77, 78, + 78, 79, 80, 81, 82, 83, 84, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 191, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, + 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, + 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, + 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, + 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, + 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, + 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, + 404, 405, 406, 407, 408, 409, 410, 411, 412, 414, 415, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, + 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, + 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, + 460, 461, 462, 463, 464, 465, 466, 468, 469, 470, 471, + 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, + 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, + 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 505, + 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, + 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, + 528, 529, 530, 531, 532, 533, 535, 536, 537, 538, 539, + 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, + 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 562, + 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, + 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, + 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, + 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 608, + 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, + 620, 621, 622, 623, 624, 625, 626, 627, 629, 630, 631, + 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, + 643, 644, 645, 646, 648, 649, 650, 651, 652, 653, 654, + 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, + 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, + 678, 679, 680, 681, 682, 683, 685, 686, 687, 688, 689, + 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, + 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, + 713, 714, 715, 716, 717, 719, 720, 721, 722, 723, 724, + 725, 726, 727, 728, 729, 730, 731, 732, 733, 735, 736, + 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, + 748, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, + 760, 761, 762, 763, 764, 766, 767, 768, 769, 770, 771, + 772, 773, 774, 775, 776, 777, 778, 779, 781, 782, 783, + 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 795, + 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, + 807, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, + 819, 820, 821, 823, 824, 825, 826, 827, 828, 829, 830, + 831, 832, 833, 834, 835, 837, 838, 839, 840, 841, 842, + 843, 844, 845, 846, 847, 848, 850, 851, 852, 853, 854, + 855, 856, 857, 858, 859, 860, 861, 863, 864, 865, 866, + 867, 868, 869, 870, 871, 872, 873, 874, 876, 877, 878, + 879, 880, 881, 882, 883, 884, 885, 886, 887, 889, 890, + 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 902, + 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 914, + 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 926, + 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 938, + 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 950, + 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 962, + 963, 964, 965, 966, 967, 968, 969, 970, 971, 973, 974, + 975, 976, 977, 978, 979, 980, 981, 982, 983, 985, 986, + 987, 988, 989, 990, 991, 992, 993, 994, 996, 997, 998, + 999, 1000, 1001, 1002, 1003, 1004, 1005, 1007, 1008, 1009, 1010, + 1011, 1012, 1013, 1014, 1015, 1016, 1018, 1019, 1020, 1021, 1022, + 1023, +}; + +static const u16 xgamma10_12[GAMMA10_TABLE_LENGTH] = { + 0, 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, + 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, + 10, 11, 11, 12, 12, 13, 14, 14, 15, 15, 16, + 17, 17, 18, 18, 19, 20, 20, 21, 22, 22, 23, + 23, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, + 31, 31, 32, 33, 33, 34, 35, 35, 36, 37, 37, + 38, 39, 40, 40, 41, 42, 42, 43, 44, 44, 45, + 46, 47, 47, 48, 49, 49, 50, 51, 52, 52, 53, + 54, 55, 55, 56, 57, 58, 58, 59, 60, 61, 61, + 62, 63, 64, 64, 65, 66, 67, 67, 68, 69, 70, + 70, 71, 72, 73, 74, 74, 75, 76, 77, 77, 78, + 79, 80, 81, 81, 82, 83, 84, 84, 85, 86, 87, + 88, 88, 89, 90, 91, 92, 92, 93, 94, 95, 96, + 96, 97, 98, 99, 100, 101, 101, 102, 103, 104, 105, + 105, 106, 107, 108, 109, 110, 110, 111, 112, 113, 114, + 115, 115, 116, 117, 118, 119, 120, 120, 121, 122, 123, + 124, 125, 125, 126, 127, 128, 129, 130, 131, 131, 132, + 133, 134, 135, 136, 137, 137, 138, 139, 140, 141, 142, + 143, 143, 144, 145, 146, 147, 148, 149, 150, 150, 151, + 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, + 162, 163, 164, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 172, 173, 174, 175, 176, 177, 178, 179, 180, 180, + 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 190, + 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 255, 256, 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, 334, 334, 335, 336, 337, + 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, + 349, 350, 351, 353, 354, 355, 356, 357, 358, 359, 360, + 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, + 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, + 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, + 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 409, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, + 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 439, + 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, + 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 462, + 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, + 474, 475, 476, 477, 478, 479, 481, 482, 483, 484, 485, + 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, + 497, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, + 509, 510, 511, 512, 513, 515, 516, 517, 518, 519, 520, + 521, 522, 523, 524, 525, 526, 527, 529, 530, 531, 532, + 533, 534, 535, 536, 537, 538, 539, 540, 541, 543, 544, + 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, + 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, + 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, + 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, + 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 604, + 605, 606, 607, 608, 609, 610, 611, 612, 613, 615, 616, + 617, 618, 619, 620, 621, 622, 623, 624, 626, 627, 628, + 629, 630, 631, 632, 633, 634, 636, 637, 638, 639, 640, + 641, 642, 643, 644, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 656, 657, 658, 659, 660, 661, 662, 663, 664, + 666, 667, 668, 669, 670, 671, 672, 673, 675, 676, 677, + 678, 679, 680, 681, 682, 683, 685, 686, 687, 688, 689, + 690, 691, 692, 694, 695, 696, 697, 698, 699, 700, 701, + 703, 704, 705, 706, 707, 708, 709, 710, 712, 713, 714, + 715, 716, 717, 718, 720, 721, 722, 723, 724, 725, 726, + 727, 729, 730, 731, 732, 733, 734, 735, 737, 738, 739, + 740, 741, 742, 743, 745, 746, 747, 748, 749, 750, 751, + 752, 754, 755, 756, 757, 758, 759, 760, 762, 763, 764, + 765, 766, 767, 768, 770, 771, 772, 773, 774, 775, 776, + 778, 779, 780, 781, 782, 783, 785, 786, 787, 788, 789, + 790, 791, 793, 794, 795, 796, 797, 798, 799, 801, 802, + 803, 804, 805, 806, 808, 809, 810, 811, 812, 813, 814, + 816, 817, 818, 819, 820, 821, 823, 824, 825, 826, 827, + 828, 830, 831, 832, 833, 834, 835, 836, 838, 839, 840, + 841, 842, 843, 845, 846, 847, 848, 849, 850, 852, 853, + 854, 855, 856, 857, 859, 860, 861, 862, 863, 864, 866, + 867, 868, 869, 870, 871, 873, 874, 875, 876, 877, 878, + 880, 881, 882, 883, 884, 885, 887, 888, 889, 890, 891, + 892, 894, 895, 896, 897, 898, 900, 901, 902, 903, 904, + 905, 907, 908, 909, 910, 911, 912, 914, 915, 916, 917, + 918, 920, 921, 922, 923, 924, 925, 927, 928, 929, 930, + 931, 932, 934, 935, 936, 937, 938, 940, 941, 942, 943, + 944, 946, 947, 948, 949, 950, 951, 953, 954, 955, 956, + 957, 959, 960, 961, 962, 963, 964, 966, 967, 968, 969, + 970, 972, 973, 974, 975, 976, 978, 979, 980, 981, 982, + 984, 985, 986, 987, 988, 989, 991, 992, 993, 994, 995, + 997, 998, 999, 1000, 1001, 1003, 1004, 1005, 1006, 1007, 1009, + 1010, 1011, 1012, 1013, 1015, 1016, 1017, 1018, 1019, 1021, 1022, + 1023, +}; + +static const u16 xgamma10_13[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, + 7, 7, 8, 8, 9, 9, 10, 10, 10, 11, 11, + 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, + 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 25, 25, 26, 26, 27, 27, 28, 28, + 29, 30, 30, 31, 31, 32, 32, 33, 34, 34, 35, + 35, 36, 37, 37, 38, 38, 39, 40, 40, 41, 42, + 42, 43, 43, 44, 45, 45, 46, 47, 47, 48, 48, + 49, 50, 50, 51, 52, 52, 53, 54, 54, 55, 56, + 56, 57, 58, 58, 59, 60, 60, 61, 62, 62, 63, + 64, 64, 65, 66, 67, 67, 68, 69, 69, 70, 71, + 71, 72, 73, 74, 74, 75, 76, 76, 77, 78, 79, + 79, 80, 81, 81, 82, 83, 84, 84, 85, 86, 87, + 87, 88, 89, 89, 90, 91, 92, 92, 93, 94, 95, + 95, 96, 97, 98, 98, 99, 100, 101, 102, 102, 103, + 104, 105, 105, 106, 107, 108, 108, 109, 110, 111, 112, + 112, 113, 114, 115, 115, 116, 117, 118, 119, 119, 120, + 121, 122, 123, 123, 124, 125, 126, 127, 127, 128, 129, + 130, 131, 131, 132, 133, 134, 135, 135, 136, 137, 138, + 139, 140, 140, 141, 142, 143, 144, 145, 145, 146, 147, + 148, 149, 149, 150, 151, 152, 153, 154, 155, 155, 156, + 157, 158, 159, 160, 160, 161, 162, 163, 164, 165, 166, + 166, 167, 168, 169, 170, 171, 172, 172, 173, 174, 175, + 176, 177, 178, 178, 179, 180, 181, 182, 183, 184, 185, + 185, 186, 187, 188, 189, 190, 191, 192, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, + 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, + 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, + 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, + 375, 376, 377, 378, 379, 380, 381, 383, 384, 385, 386, + 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, + 398, 399, 400, 401, 402, 403, 404, 405, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, + 421, 422, 423, 424, 426, 427, 428, 429, 430, 431, 432, + 433, 434, 435, 436, 437, 438, 439, 440, 442, 443, 444, + 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, + 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, + 468, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, + 480, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, + 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 504, + 505, 506, 507, 508, 509, 510, 511, 512, 513, 515, 516, + 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, + 529, 530, 531, 532, 534, 535, 536, 537, 538, 539, 540, + 541, 542, 544, 545, 546, 547, 548, 549, 550, 551, 553, + 554, 555, 556, 557, 558, 559, 561, 562, 563, 564, 565, + 566, 567, 568, 570, 571, 572, 573, 574, 575, 576, 578, + 579, 580, 581, 582, 583, 584, 586, 587, 588, 589, 590, + 591, 592, 594, 595, 596, 597, 598, 599, 600, 602, 603, + 604, 605, 606, 607, 608, 610, 611, 612, 613, 614, 615, + 617, 618, 619, 620, 621, 622, 624, 625, 626, 627, 628, + 629, 630, 632, 633, 634, 635, 636, 637, 639, 640, 641, + 642, 643, 644, 646, 647, 648, 649, 650, 652, 653, 654, + 655, 656, 657, 659, 660, 661, 662, 663, 664, 666, 667, + 668, 669, 670, 671, 673, 674, 675, 676, 677, 679, 680, + 681, 682, 683, 684, 686, 687, 688, 689, 690, 692, 693, + 694, 695, 696, 698, 699, 700, 701, 702, 704, 705, 706, + 707, 708, 709, 711, 712, 713, 714, 715, 717, 718, 719, + 720, 721, 723, 724, 725, 726, 727, 729, 730, 731, 732, + 733, 735, 736, 737, 738, 739, 741, 742, 743, 744, 746, + 747, 748, 749, 750, 752, 753, 754, 755, 756, 758, 759, + 760, 761, 762, 764, 765, 766, 767, 769, 770, 771, 772, + 773, 775, 776, 777, 778, 780, 781, 782, 783, 784, 786, + 787, 788, 789, 791, 792, 793, 794, 795, 797, 798, 799, + 800, 802, 803, 804, 805, 807, 808, 809, 810, 811, 813, + 814, 815, 816, 818, 819, 820, 821, 823, 824, 825, 826, + 827, 829, 830, 831, 832, 834, 835, 836, 837, 839, 840, + 841, 842, 844, 845, 846, 847, 849, 850, 851, 852, 854, + 855, 856, 857, 859, 860, 861, 862, 864, 865, 866, 867, + 869, 870, 871, 872, 874, 875, 876, 877, 879, 880, 881, + 882, 884, 885, 886, 887, 889, 890, 891, 892, 894, 895, + 896, 897, 899, 900, 901, 903, 904, 905, 906, 908, 909, + 910, 911, 913, 914, 915, 916, 918, 919, 920, 922, 923, + 924, 925, 927, 928, 929, 930, 932, 933, 934, 935, 937, + 938, 939, 941, 942, 943, 944, 946, 947, 948, 950, 951, + 952, 953, 955, 956, 957, 958, 960, 961, 962, 964, 965, + 966, 967, 969, 970, 971, 973, 974, 975, 976, 978, 979, + 980, 982, 983, 984, 985, 987, 988, 989, 991, 992, 993, + 994, 996, 997, 998, 1000, 1001, 1002, 1004, 1005, 1006, 1007, + 1009, 1010, 1011, 1013, 1014, 1015, 1017, 1018, 1019, 1020, 1022, + 1023, +}; + +static const u16 xgamma10_14[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, + 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, + 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, + 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, + 17, 18, 18, 18, 19, 19, 20, 20, 21, 21, 22, + 22, 23, 23, 23, 24, 24, 25, 25, 26, 26, 27, + 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, + 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, + 39, 39, 40, 41, 41, 42, 42, 43, 43, 44, 45, + 45, 46, 46, 47, 47, 48, 49, 49, 50, 50, 51, + 52, 52, 53, 53, 54, 55, 55, 56, 56, 57, 58, + 58, 59, 59, 60, 61, 61, 62, 63, 63, 64, 64, + 65, 66, 66, 67, 68, 68, 69, 70, 70, 71, 72, + 72, 73, 74, 74, 75, 76, 76, 77, 78, 78, 79, + 80, 80, 81, 82, 82, 83, 84, 84, 85, 86, 86, + 87, 88, 88, 89, 90, 91, 91, 92, 93, 93, 94, + 95, 95, 96, 97, 98, 98, 99, 100, 100, 101, 102, + 103, 103, 104, 105, 106, 106, 107, 108, 109, 109, 110, + 111, 111, 112, 113, 114, 114, 115, 116, 117, 117, 118, + 119, 120, 120, 121, 122, 123, 124, 124, 125, 126, 127, + 127, 128, 129, 130, 130, 131, 132, 133, 134, 134, 135, + 136, 137, 138, 138, 139, 140, 141, 141, 142, 143, 144, + 145, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, + 154, 154, 155, 156, 157, 158, 158, 159, 160, 161, 162, + 163, 163, 164, 165, 166, 167, 168, 168, 169, 170, 171, + 172, 173, 173, 174, 175, 176, 177, 178, 179, 179, 180, + 181, 182, 183, 184, 185, 185, 186, 187, 188, 189, 190, + 191, 191, 192, 193, 194, 195, 196, 197, 198, 198, 199, + 200, 201, 202, 203, 204, 205, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, + 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, + 347, 348, 349, 351, 352, 353, 354, 355, 356, 357, 358, + 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, + 370, 371, 372, 373, 374, 375, 377, 378, 379, 380, 381, + 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, + 393, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 410, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 423, 424, 425, 426, 427, 428, + 429, 430, 431, 432, 433, 435, 436, 437, 438, 439, 440, + 441, 442, 443, 444, 446, 447, 448, 449, 450, 451, 452, + 453, 454, 456, 457, 458, 459, 460, 461, 462, 463, 464, + 466, 467, 468, 469, 470, 471, 472, 473, 475, 476, 477, + 478, 479, 480, 481, 482, 484, 485, 486, 487, 488, 489, + 490, 491, 493, 494, 495, 496, 497, 498, 499, 501, 502, + 503, 504, 505, 506, 507, 509, 510, 511, 512, 513, 514, + 515, 517, 518, 519, 520, 521, 522, 524, 525, 526, 527, + 528, 529, 531, 532, 533, 534, 535, 536, 537, 539, 540, + 541, 542, 543, 544, 546, 547, 548, 549, 550, 552, 553, + 554, 555, 556, 557, 559, 560, 561, 562, 563, 564, 566, + 567, 568, 569, 570, 572, 573, 574, 575, 576, 578, 579, + 580, 581, 582, 583, 585, 586, 587, 588, 589, 591, 592, + 593, 594, 595, 597, 598, 599, 600, 601, 603, 604, 605, + 606, 607, 609, 610, 611, 612, 613, 615, 616, 617, 618, + 620, 621, 622, 623, 624, 626, 627, 628, 629, 630, 632, + 633, 634, 635, 637, 638, 639, 640, 641, 643, 644, 645, + 646, 648, 649, 650, 651, 653, 654, 655, 656, 657, 659, + 660, 661, 662, 664, 665, 666, 667, 669, 670, 671, 672, + 674, 675, 676, 677, 679, 680, 681, 682, 684, 685, 686, + 687, 689, 690, 691, 692, 694, 695, 696, 697, 699, 700, + 701, 702, 704, 705, 706, 707, 709, 710, 711, 712, 714, + 715, 716, 717, 719, 720, 721, 723, 724, 725, 726, 728, + 729, 730, 731, 733, 734, 735, 737, 738, 739, 740, 742, + 743, 744, 745, 747, 748, 749, 751, 752, 753, 754, 756, + 757, 758, 760, 761, 762, 763, 765, 766, 767, 769, 770, + 771, 772, 774, 775, 776, 778, 779, 780, 782, 783, 784, + 785, 787, 788, 789, 791, 792, 793, 794, 796, 797, 798, + 800, 801, 802, 804, 805, 806, 808, 809, 810, 811, 813, + 814, 815, 817, 818, 819, 821, 822, 823, 825, 826, 827, + 829, 830, 831, 833, 834, 835, 836, 838, 839, 840, 842, + 843, 844, 846, 847, 848, 850, 851, 852, 854, 855, 856, + 858, 859, 860, 862, 863, 864, 866, 867, 868, 870, 871, + 872, 874, 875, 876, 878, 879, 880, 882, 883, 884, 886, + 887, 888, 890, 891, 893, 894, 895, 897, 898, 899, 901, + 902, 903, 905, 906, 907, 909, 910, 911, 913, 914, 915, + 917, 918, 920, 921, 922, 924, 925, 926, 928, 929, 930, + 932, 933, 935, 936, 937, 939, 940, 941, 943, 944, 945, + 947, 948, 950, 951, 952, 954, 955, 956, 958, 959, 961, + 962, 963, 965, 966, 967, 969, 970, 972, 973, 974, 976, + 977, 978, 980, 981, 983, 984, 985, 987, 988, 990, 991, + 992, 994, 995, 996, 998, 999, 1001, 1002, 1003, 1005, 1006, + 1008, 1009, 1010, 1012, 1013, 1015, 1016, 1017, 1019, 1020, 1022, + 1023, +}; + +static const u16 xgamma10_15[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, + 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, + 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, + 13, 13, 13, 14, 14, 15, 15, 15, 16, 16, 16, + 17, 17, 18, 18, 18, 19, 19, 20, 20, 20, 21, + 21, 22, 22, 22, 23, 23, 24, 24, 25, 25, 25, + 26, 26, 27, 27, 28, 28, 28, 29, 29, 30, 30, + 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, + 36, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, + 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, + 47, 48, 48, 49, 50, 50, 51, 51, 52, 52, 53, + 53, 54, 55, 55, 56, 56, 57, 57, 58, 59, 59, + 60, 60, 61, 62, 62, 63, 63, 64, 64, 65, 66, + 66, 67, 67, 68, 69, 69, 70, 71, 71, 72, 72, + 73, 74, 74, 75, 76, 76, 77, 77, 78, 79, 79, + 80, 81, 81, 82, 83, 83, 84, 84, 85, 86, 86, + 87, 88, 88, 89, 90, 90, 91, 92, 92, 93, 94, + 94, 95, 96, 97, 97, 98, 99, 99, 100, 101, 101, + 102, 103, 103, 104, 105, 106, 106, 107, 108, 108, 109, + 110, 110, 111, 112, 113, 113, 114, 115, 116, 116, 117, + 118, 118, 119, 120, 121, 121, 122, 123, 124, 124, 125, + 126, 127, 127, 128, 129, 130, 130, 131, 132, 133, 133, + 134, 135, 136, 136, 137, 138, 139, 139, 140, 141, 142, + 143, 143, 144, 145, 146, 146, 147, 148, 149, 150, 150, + 151, 152, 153, 154, 154, 155, 156, 157, 158, 158, 159, + 160, 161, 162, 162, 163, 164, 165, 166, 167, 167, 168, + 169, 170, 171, 171, 172, 173, 174, 175, 176, 176, 177, + 178, 179, 180, 181, 181, 182, 183, 184, 185, 186, 187, + 187, 188, 189, 190, 191, 192, 193, 193, 194, 195, 196, + 197, 198, 199, 199, 200, 201, 202, 203, 204, 205, 206, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, + 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, + 344, 345, 346, 347, 349, 350, 351, 352, 353, 354, 355, + 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, + 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, + 379, 380, 381, 383, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 395, 396, 397, 398, 399, 400, 401, 402, + 403, 404, 405, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 417, 418, 419, 420, 421, 422, 423, 424, 425, 427, + 428, 429, 430, 431, 432, 433, 434, 436, 437, 438, 439, + 440, 441, 442, 444, 445, 446, 447, 448, 449, 450, 451, + 453, 454, 455, 456, 457, 458, 460, 461, 462, 463, 464, + 465, 466, 468, 469, 470, 471, 472, 473, 475, 476, 477, + 478, 479, 480, 482, 483, 484, 485, 486, 487, 489, 490, + 491, 492, 493, 494, 496, 497, 498, 499, 500, 501, 503, + 504, 505, 506, 507, 509, 510, 511, 512, 513, 515, 516, + 517, 518, 519, 521, 522, 523, 524, 525, 527, 528, 529, + 530, 531, 533, 534, 535, 536, 537, 539, 540, 541, 542, + 543, 545, 546, 547, 548, 550, 551, 552, 553, 554, 556, + 557, 558, 559, 561, 562, 563, 564, 565, 567, 568, 569, + 570, 572, 573, 574, 575, 577, 578, 579, 580, 582, 583, + 584, 585, 587, 588, 589, 590, 591, 593, 594, 595, 596, + 598, 599, 600, 602, 603, 604, 605, 607, 608, 609, 610, + 612, 613, 614, 615, 617, 618, 619, 620, 622, 623, 624, + 626, 627, 628, 629, 631, 632, 633, 634, 636, 637, 638, + 640, 641, 642, 643, 645, 646, 647, 649, 650, 651, 652, + 654, 655, 656, 658, 659, 660, 662, 663, 664, 665, 667, + 668, 669, 671, 672, 673, 675, 676, 677, 678, 680, 681, + 682, 684, 685, 686, 688, 689, 690, 692, 693, 694, 696, + 697, 698, 700, 701, 702, 703, 705, 706, 707, 709, 710, + 711, 713, 714, 715, 717, 718, 719, 721, 722, 723, 725, + 726, 727, 729, 730, 731, 733, 734, 735, 737, 738, 740, + 741, 742, 744, 745, 746, 748, 749, 750, 752, 753, 754, + 756, 757, 758, 760, 761, 763, 764, 765, 767, 768, 769, + 771, 772, 773, 775, 776, 778, 779, 780, 782, 783, 784, + 786, 787, 789, 790, 791, 793, 794, 795, 797, 798, 800, + 801, 802, 804, 805, 806, 808, 809, 811, 812, 813, 815, + 816, 818, 819, 820, 822, 823, 825, 826, 827, 829, 830, + 832, 833, 834, 836, 837, 839, 840, 841, 843, 844, 846, + 847, 848, 850, 851, 853, 854, 855, 857, 858, 860, 861, + 863, 864, 865, 867, 868, 870, 871, 872, 874, 875, 877, + 878, 880, 881, 882, 884, 885, 887, 888, 890, 891, 892, + 894, 895, 897, 898, 900, 901, 902, 904, 905, 907, 908, + 910, 911, 913, 914, 915, 917, 918, 920, 921, 923, 924, + 926, 927, 929, 930, 931, 933, 934, 936, 937, 939, 940, + 942, 943, 945, 946, 947, 949, 950, 952, 953, 955, 956, + 958, 959, 961, 962, 964, 965, 967, 968, 969, 971, 972, + 974, 975, 977, 978, 980, 981, 983, 984, 986, 987, 989, + 990, 992, 993, 995, 996, 998, 999, 1001, 1002, 1004, 1005, + 1007, 1008, 1010, 1011, 1013, 1014, 1016, 1017, 1019, 1020, 1022, + 1023, +}; + +static const u16 xgamma10_16[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, + 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, + 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, + 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, + 20, 21, 21, 21, 22, 22, 22, 23, 23, 24, 24, + 24, 25, 25, 26, 26, 26, 27, 27, 28, 28, 28, + 29, 29, 30, 30, 31, 31, 31, 32, 32, 33, 33, + 34, 34, 35, 35, 35, 36, 36, 37, 37, 38, 38, + 39, 39, 40, 40, 41, 41, 41, 42, 42, 43, 43, + 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, + 49, 50, 50, 51, 52, 52, 53, 53, 54, 54, 55, + 55, 56, 56, 57, 57, 58, 58, 59, 60, 60, 61, + 61, 62, 62, 63, 63, 64, 65, 65, 66, 66, 67, + 67, 68, 69, 69, 70, 70, 71, 72, 72, 73, 73, + 74, 75, 75, 76, 76, 77, 78, 78, 79, 79, 80, + 81, 81, 82, 82, 83, 84, 84, 85, 86, 86, 87, + 87, 88, 89, 89, 90, 91, 91, 92, 93, 93, 94, + 95, 95, 96, 97, 97, 98, 99, 99, 100, 101, 101, + 102, 103, 103, 104, 105, 105, 106, 107, 107, 108, 109, + 109, 110, 111, 111, 112, 113, 114, 114, 115, 116, 116, + 117, 118, 119, 119, 120, 121, 121, 122, 123, 124, 124, + 125, 126, 126, 127, 128, 129, 129, 130, 131, 132, 132, + 133, 134, 135, 135, 136, 137, 138, 138, 139, 140, 141, + 141, 142, 143, 144, 144, 145, 146, 147, 148, 148, 149, + 150, 151, 151, 152, 153, 154, 155, 155, 156, 157, 158, + 159, 159, 160, 161, 162, 163, 163, 164, 165, 166, 167, + 167, 168, 169, 170, 171, 171, 172, 173, 174, 175, 176, + 176, 177, 178, 179, 180, 181, 181, 182, 183, 184, 185, + 186, 186, 187, 188, 189, 190, 191, 192, 192, 193, 194, + 195, 196, 197, 198, 198, 199, 200, 201, 202, 203, 204, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 325, 326, 327, 329, 330, 331, + 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, + 343, 344, 345, 346, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 359, 360, 362, 363, 364, 365, 366, + 367, 368, 369, 370, 371, 372, 374, 375, 376, 377, 378, + 379, 380, 381, 382, 383, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 395, 396, 397, 398, 399, 400, 401, 402, + 404, 405, 406, 407, 408, 409, 410, 411, 413, 414, 415, + 416, 417, 418, 419, 421, 422, 423, 424, 425, 426, 428, + 429, 430, 431, 432, 433, 434, 436, 437, 438, 439, 440, + 441, 443, 444, 445, 446, 447, 448, 450, 451, 452, 453, + 454, 456, 457, 458, 459, 460, 461, 463, 464, 465, 466, + 467, 469, 470, 471, 472, 473, 475, 476, 477, 478, 479, + 481, 482, 483, 484, 485, 487, 488, 489, 490, 491, 493, + 494, 495, 496, 498, 499, 500, 501, 502, 504, 505, 506, + 507, 509, 510, 511, 512, 514, 515, 516, 517, 519, 520, + 521, 522, 523, 525, 526, 527, 528, 530, 531, 532, 533, + 535, 536, 537, 538, 540, 541, 542, 544, 545, 546, 547, + 549, 550, 551, 552, 554, 555, 556, 557, 559, 560, 561, + 563, 564, 565, 566, 568, 569, 570, 572, 573, 574, 575, + 577, 578, 579, 581, 582, 583, 584, 586, 587, 588, 590, + 591, 592, 594, 595, 596, 598, 599, 600, 601, 603, 604, + 605, 607, 608, 609, 611, 612, 613, 615, 616, 617, 619, + 620, 621, 623, 624, 625, 627, 628, 629, 631, 632, 633, + 635, 636, 637, 639, 640, 641, 643, 644, 645, 647, 648, + 649, 651, 652, 653, 655, 656, 657, 659, 660, 662, 663, + 664, 666, 667, 668, 670, 671, 672, 674, 675, 677, 678, + 679, 681, 682, 683, 685, 686, 688, 689, 690, 692, 693, + 694, 696, 697, 699, 700, 701, 703, 704, 706, 707, 708, + 710, 711, 712, 714, 715, 717, 718, 719, 721, 722, 724, + 725, 727, 728, 729, 731, 732, 734, 735, 736, 738, 739, + 741, 742, 743, 745, 746, 748, 749, 751, 752, 753, 755, + 756, 758, 759, 761, 762, 763, 765, 766, 768, 769, 771, + 772, 774, 775, 776, 778, 779, 781, 782, 784, 785, 787, + 788, 789, 791, 792, 794, 795, 797, 798, 800, 801, 803, + 804, 805, 807, 808, 810, 811, 813, 814, 816, 817, 819, + 820, 822, 823, 825, 826, 827, 829, 830, 832, 833, 835, + 836, 838, 839, 841, 842, 844, 845, 847, 848, 850, 851, + 853, 854, 856, 857, 859, 860, 862, 863, 865, 866, 868, + 869, 871, 872, 874, 875, 877, 878, 880, 881, 883, 884, + 886, 887, 889, 890, 892, 893, 895, 897, 898, 900, 901, + 903, 904, 906, 907, 909, 910, 912, 913, 915, 916, 918, + 919, 921, 923, 924, 926, 927, 929, 930, 932, 933, 935, + 936, 938, 940, 941, 943, 944, 946, 947, 949, 950, 952, + 954, 955, 957, 958, 960, 961, 963, 964, 966, 968, 969, + 971, 972, 974, 975, 977, 979, 980, 982, 983, 985, 986, + 988, 990, 991, 993, 994, 996, 998, 999, 1001, 1002, 1004, + 1005, 1007, 1009, 1010, 1012, 1013, 1015, 1017, 1018, 1020, 1021, + 1023, +}; + +static const u16 xgamma10_17[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, + 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 15, + 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, + 19, 20, 20, 20, 21, 21, 21, 22, 22, 22, 23, + 23, 23, 24, 24, 25, 25, 25, 26, 26, 26, 27, + 27, 28, 28, 28, 29, 29, 29, 30, 30, 31, 31, + 31, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, + 36, 37, 37, 37, 38, 38, 39, 39, 40, 40, 40, + 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, + 46, 46, 47, 47, 48, 48, 49, 49, 50, 50, 51, + 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, + 57, 57, 58, 58, 59, 60, 60, 61, 61, 62, 62, + 63, 63, 64, 64, 65, 65, 66, 67, 67, 68, 68, + 69, 69, 70, 70, 71, 72, 72, 73, 73, 74, 74, + 75, 76, 76, 77, 77, 78, 79, 79, 80, 80, 81, + 82, 82, 83, 83, 84, 85, 85, 86, 86, 87, 88, + 88, 89, 89, 90, 91, 91, 92, 93, 93, 94, 95, + 95, 96, 96, 97, 98, 98, 99, 100, 100, 101, 102, + 102, 103, 104, 104, 105, 106, 106, 107, 108, 108, 109, + 110, 110, 111, 112, 112, 113, 114, 114, 115, 116, 116, + 117, 118, 119, 119, 120, 121, 121, 122, 123, 124, 124, + 125, 126, 126, 127, 128, 129, 129, 130, 131, 131, 132, + 133, 134, 134, 135, 136, 137, 137, 138, 139, 140, 140, + 141, 142, 143, 143, 144, 145, 146, 146, 147, 148, 149, + 149, 150, 151, 152, 153, 153, 154, 155, 156, 156, 157, + 158, 159, 160, 160, 161, 162, 163, 164, 164, 165, 166, + 167, 168, 168, 169, 170, 171, 172, 172, 173, 174, 175, + 176, 177, 177, 178, 179, 180, 181, 182, 182, 183, 184, + 185, 186, 187, 187, 188, 189, 190, 191, 192, 193, 193, + 194, 195, 196, 197, 198, 199, 199, 200, 201, 202, 203, + 204, 205, 206, 206, 207, 208, 209, 210, 211, 212, 213, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 319, 320, + 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, 334, 336, 337, 338, 339, 340, 341, 342, 343, + 344, 345, 346, 347, 349, 350, 351, 352, 353, 354, 355, + 356, 357, 358, 360, 361, 362, 363, 364, 365, 366, 367, + 368, 370, 371, 372, 373, 374, 375, 376, 377, 379, 380, + 381, 382, 383, 384, 385, 386, 388, 389, 390, 391, 392, + 393, 394, 396, 397, 398, 399, 400, 401, 403, 404, 405, + 406, 407, 408, 409, 411, 412, 413, 414, 415, 417, 418, + 419, 420, 421, 422, 424, 425, 426, 427, 428, 430, 431, + 432, 433, 434, 435, 437, 438, 439, 440, 441, 443, 444, + 445, 446, 448, 449, 450, 451, 452, 454, 455, 456, 457, + 458, 460, 461, 462, 463, 465, 466, 467, 468, 469, 471, + 472, 473, 474, 476, 477, 478, 479, 481, 482, 483, 484, + 486, 487, 488, 489, 491, 492, 493, 494, 496, 497, 498, + 499, 501, 502, 503, 505, 506, 507, 508, 510, 511, 512, + 513, 515, 516, 517, 519, 520, 521, 522, 524, 525, 526, + 528, 529, 530, 532, 533, 534, 535, 537, 538, 539, 541, + 542, 543, 545, 546, 547, 549, 550, 551, 552, 554, 555, + 556, 558, 559, 560, 562, 563, 564, 566, 567, 568, 570, + 571, 572, 574, 575, 576, 578, 579, 580, 582, 583, 584, + 586, 587, 589, 590, 591, 593, 594, 595, 597, 598, 599, + 601, 602, 604, 605, 606, 608, 609, 610, 612, 613, 615, + 616, 617, 619, 620, 621, 623, 624, 626, 627, 628, 630, + 631, 633, 634, 635, 637, 638, 640, 641, 642, 644, 645, + 647, 648, 649, 651, 652, 654, 655, 656, 658, 659, 661, + 662, 664, 665, 666, 668, 669, 671, 672, 674, 675, 676, + 678, 679, 681, 682, 684, 685, 686, 688, 689, 691, 692, + 694, 695, 697, 698, 699, 701, 702, 704, 705, 707, 708, + 710, 711, 713, 714, 716, 717, 718, 720, 721, 723, 724, + 726, 727, 729, 730, 732, 733, 735, 736, 738, 739, 741, + 742, 744, 745, 747, 748, 750, 751, 753, 754, 756, 757, + 759, 760, 762, 763, 765, 766, 768, 769, 771, 772, 774, + 775, 777, 778, 780, 781, 783, 784, 786, 787, 789, 790, + 792, 793, 795, 797, 798, 800, 801, 803, 804, 806, 807, + 809, 810, 812, 814, 815, 817, 818, 820, 821, 823, 824, + 826, 827, 829, 831, 832, 834, 835, 837, 838, 840, 842, + 843, 845, 846, 848, 849, 851, 853, 854, 856, 857, 859, + 860, 862, 864, 865, 867, 868, 870, 872, 873, 875, 876, + 878, 880, 881, 883, 884, 886, 888, 889, 891, 892, 894, + 896, 897, 899, 900, 902, 904, 905, 907, 908, 910, 912, + 913, 915, 917, 918, 920, 921, 923, 925, 926, 928, 930, + 931, 933, 935, 936, 938, 939, 941, 943, 944, 946, 948, + 949, 951, 953, 954, 956, 958, 959, 961, 963, 964, 966, + 968, 969, 971, 973, 974, 976, 978, 979, 981, 983, 984, + 986, 988, 989, 991, 993, 994, 996, 998, 999, 1001, 1003, + 1004, 1006, 1008, 1009, 1011, 1013, 1015, 1016, 1018, 1020, 1021, + 1023, +}; + +static const u16 xgamma10_18[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, + 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, + 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, + 18, 19, 19, 19, 20, 20, 20, 21, 21, 21, 22, + 22, 22, 23, 23, 23, 24, 24, 24, 25, 25, 25, + 26, 26, 26, 27, 27, 27, 28, 28, 29, 29, 29, + 30, 30, 30, 31, 31, 32, 32, 32, 33, 33, 33, + 34, 34, 35, 35, 35, 36, 36, 37, 37, 38, 38, + 38, 39, 39, 40, 40, 40, 41, 41, 42, 42, 43, + 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, + 48, 48, 49, 49, 50, 50, 51, 51, 52, 52, 53, + 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, + 59, 59, 60, 60, 61, 61, 62, 62, 63, 63, 64, + 64, 65, 65, 66, 66, 67, 68, 68, 69, 69, 70, + 70, 71, 71, 72, 72, 73, 74, 74, 75, 75, 76, + 76, 77, 78, 78, 79, 79, 80, 80, 81, 82, 82, + 83, 83, 84, 85, 85, 86, 86, 87, 88, 88, 89, + 89, 90, 91, 91, 92, 92, 93, 94, 94, 95, 96, + 96, 97, 97, 98, 99, 99, 100, 101, 101, 102, 103, + 103, 104, 104, 105, 106, 106, 107, 108, 108, 109, 110, + 110, 111, 112, 112, 113, 114, 114, 115, 116, 117, 117, + 118, 119, 119, 120, 121, 121, 122, 123, 123, 124, 125, + 126, 126, 127, 128, 128, 129, 130, 131, 131, 132, 133, + 133, 134, 135, 136, 136, 137, 138, 139, 139, 140, 141, + 142, 142, 143, 144, 145, 145, 146, 147, 148, 148, 149, + 150, 151, 151, 152, 153, 154, 155, 155, 156, 157, 158, + 158, 159, 160, 161, 162, 162, 163, 164, 165, 166, 166, + 167, 168, 169, 170, 170, 171, 172, 173, 174, 175, 175, + 176, 177, 178, 179, 179, 180, 181, 182, 183, 184, 184, + 185, 186, 187, 188, 189, 190, 190, 191, 192, 193, 194, + 195, 196, 196, 197, 198, 199, 200, 201, 202, 203, 203, + 204, 205, 206, 207, 208, 209, 210, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, + 311, 312, 313, 314, 315, 316, 317, 319, 320, 321, 322, + 323, 324, 325, 326, 327, 328, 329, 330, 331, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 344, 345, 346, + 347, 348, 349, 350, 351, 353, 354, 355, 356, 357, 358, + 359, 360, 362, 363, 364, 365, 366, 367, 368, 370, 371, + 372, 373, 374, 375, 376, 378, 379, 380, 381, 382, 383, + 385, 386, 387, 388, 389, 390, 392, 393, 394, 395, 396, + 397, 399, 400, 401, 402, 403, 405, 406, 407, 408, 409, + 411, 412, 413, 414, 415, 417, 418, 419, 420, 421, 423, + 424, 425, 426, 427, 429, 430, 431, 432, 434, 435, 436, + 437, 439, 440, 441, 442, 443, 445, 446, 447, 448, 450, + 451, 452, 453, 455, 456, 457, 458, 460, 461, 462, 464, + 465, 466, 467, 469, 470, 471, 472, 474, 475, 476, 478, + 479, 480, 481, 483, 484, 485, 487, 488, 489, 490, 492, + 493, 494, 496, 497, 498, 500, 501, 502, 504, 505, 506, + 507, 509, 510, 511, 513, 514, 515, 517, 518, 519, 521, + 522, 523, 525, 526, 527, 529, 530, 531, 533, 534, 535, + 537, 538, 540, 541, 542, 544, 545, 546, 548, 549, 550, + 552, 553, 555, 556, 557, 559, 560, 561, 563, 564, 566, + 567, 568, 570, 571, 572, 574, 575, 577, 578, 579, 581, + 582, 584, 585, 586, 588, 589, 591, 592, 594, 595, 596, + 598, 599, 601, 602, 603, 605, 606, 608, 609, 611, 612, + 613, 615, 616, 618, 619, 621, 622, 624, 625, 626, 628, + 629, 631, 632, 634, 635, 637, 638, 640, 641, 642, 644, + 645, 647, 648, 650, 651, 653, 654, 656, 657, 659, 660, + 662, 663, 665, 666, 668, 669, 671, 672, 673, 675, 676, + 678, 679, 681, 682, 684, 686, 687, 689, 690, 692, 693, + 695, 696, 698, 699, 701, 702, 704, 705, 707, 708, 710, + 711, 713, 714, 716, 717, 719, 721, 722, 724, 725, 727, + 728, 730, 731, 733, 734, 736, 738, 739, 741, 742, 744, + 745, 747, 749, 750, 752, 753, 755, 756, 758, 760, 761, + 763, 764, 766, 767, 769, 771, 772, 774, 775, 777, 779, + 780, 782, 783, 785, 787, 788, 790, 791, 793, 795, 796, + 798, 799, 801, 803, 804, 806, 807, 809, 811, 812, 814, + 816, 817, 819, 820, 822, 824, 825, 827, 829, 830, 832, + 834, 835, 837, 839, 840, 842, 843, 845, 847, 848, 850, + 852, 853, 855, 857, 858, 860, 862, 863, 865, 867, 868, + 870, 872, 873, 875, 877, 878, 880, 882, 884, 885, 887, + 889, 890, 892, 894, 895, 897, 899, 900, 902, 904, 906, + 907, 909, 911, 912, 914, 916, 918, 919, 921, 923, 924, + 926, 928, 930, 931, 933, 935, 936, 938, 940, 942, 943, + 945, 947, 949, 950, 952, 954, 956, 957, 959, 961, 963, + 964, 966, 968, 970, 971, 973, 975, 977, 978, 980, 982, + 984, 986, 987, 989, 991, 993, 994, 996, 998, 1000, 1002, + 1003, 1005, 1007, 1009, 1010, 1012, 1014, 1016, 1018, 1019, 1021, + 1023, +}; + +static const u16 xgamma10_19[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, + 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, + 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, + 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, + 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 21, + 21, 21, 22, 22, 22, 22, 23, 23, 23, 24, 24, + 24, 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, + 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 32, + 32, 32, 33, 33, 33, 34, 34, 35, 35, 35, 36, + 36, 36, 37, 37, 38, 38, 38, 39, 39, 40, 40, + 41, 41, 41, 42, 42, 43, 43, 43, 44, 44, 45, + 45, 46, 46, 46, 47, 47, 48, 48, 49, 49, 50, + 50, 51, 51, 51, 52, 52, 53, 53, 54, 54, 55, + 55, 56, 56, 57, 57, 58, 58, 59, 59, 60, 60, + 61, 61, 62, 62, 63, 63, 64, 64, 65, 65, 66, + 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71, + 72, 72, 73, 74, 74, 75, 75, 76, 76, 77, 77, + 78, 79, 79, 80, 80, 81, 81, 82, 83, 83, 84, + 84, 85, 85, 86, 87, 87, 88, 88, 89, 90, 90, + 91, 91, 92, 93, 93, 94, 94, 95, 96, 96, 97, + 98, 98, 99, 99, 100, 101, 101, 102, 103, 103, 104, + 105, 105, 106, 107, 107, 108, 108, 109, 110, 110, 111, + 112, 112, 113, 114, 114, 115, 116, 116, 117, 118, 119, + 119, 120, 121, 121, 122, 123, 123, 124, 125, 125, 126, + 127, 128, 128, 129, 130, 130, 131, 132, 133, 133, 134, + 135, 135, 136, 137, 138, 138, 139, 140, 141, 141, 142, + 143, 144, 144, 145, 146, 147, 147, 148, 149, 150, 150, + 151, 152, 153, 154, 154, 155, 156, 157, 157, 158, 159, + 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, + 169, 169, 170, 171, 172, 173, 173, 174, 175, 176, 177, + 178, 178, 179, 180, 181, 182, 183, 183, 184, 185, 186, + 187, 188, 188, 189, 190, 191, 192, 193, 194, 195, 195, + 196, 197, 198, 199, 200, 201, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, + 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 299, 300, 301, 302, + 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 326, + 327, 328, 329, 330, 331, 332, 333, 334, 336, 337, 338, + 339, 340, 341, 342, 343, 345, 346, 347, 348, 349, 350, + 351, 353, 354, 355, 356, 357, 358, 360, 361, 362, 363, + 364, 365, 367, 368, 369, 370, 371, 372, 374, 375, 376, + 377, 378, 379, 381, 382, 383, 384, 385, 387, 388, 389, + 390, 391, 393, 394, 395, 396, 397, 399, 400, 401, 402, + 404, 405, 406, 407, 408, 410, 411, 412, 413, 415, 416, + 417, 418, 420, 421, 422, 423, 425, 426, 427, 428, 430, + 431, 432, 433, 435, 436, 437, 439, 440, 441, 442, 444, + 445, 446, 447, 449, 450, 451, 453, 454, 455, 456, 458, + 459, 460, 462, 463, 464, 466, 467, 468, 470, 471, 472, + 473, 475, 476, 477, 479, 480, 481, 483, 484, 485, 487, + 488, 489, 491, 492, 493, 495, 496, 498, 499, 500, 502, + 503, 504, 506, 507, 508, 510, 511, 512, 514, 515, 517, + 518, 519, 521, 522, 523, 525, 526, 528, 529, 530, 532, + 533, 535, 536, 537, 539, 540, 542, 543, 544, 546, 547, + 549, 550, 551, 553, 554, 556, 557, 559, 560, 561, 563, + 564, 566, 567, 569, 570, 572, 573, 574, 576, 577, 579, + 580, 582, 583, 585, 586, 587, 589, 590, 592, 593, 595, + 596, 598, 599, 601, 602, 604, 605, 607, 608, 610, 611, + 613, 614, 616, 617, 619, 620, 622, 623, 625, 626, 628, + 629, 631, 632, 634, 635, 637, 638, 640, 641, 643, 644, + 646, 647, 649, 650, 652, 653, 655, 656, 658, 660, 661, + 663, 664, 666, 667, 669, 670, 672, 674, 675, 677, 678, + 680, 681, 683, 684, 686, 688, 689, 691, 692, 694, 696, + 697, 699, 700, 702, 703, 705, 707, 708, 710, 711, 713, + 715, 716, 718, 719, 721, 723, 724, 726, 728, 729, 731, + 732, 734, 736, 737, 739, 741, 742, 744, 745, 747, 749, + 750, 752, 754, 755, 757, 759, 760, 762, 764, 765, 767, + 768, 770, 772, 773, 775, 777, 778, 780, 782, 783, 785, + 787, 789, 790, 792, 794, 795, 797, 799, 800, 802, 804, + 805, 807, 809, 810, 812, 814, 816, 817, 819, 821, 822, + 824, 826, 828, 829, 831, 833, 834, 836, 838, 840, 841, + 843, 845, 847, 848, 850, 852, 854, 855, 857, 859, 861, + 862, 864, 866, 868, 869, 871, 873, 875, 876, 878, 880, + 882, 883, 885, 887, 889, 891, 892, 894, 896, 898, 899, + 901, 903, 905, 907, 908, 910, 912, 914, 916, 917, 919, + 921, 923, 925, 926, 928, 930, 932, 934, 936, 937, 939, + 941, 943, 945, 947, 948, 950, 952, 954, 956, 958, 959, + 961, 963, 965, 967, 969, 970, 972, 974, 976, 978, 980, + 982, 983, 985, 987, 989, 991, 993, 995, 997, 998, 1000, + 1002, 1004, 1006, 1008, 1010, 1012, 1014, 1015, 1017, 1019, 1021, + 1023, +}; + +static const u16 xgamma10_20[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, + 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, + 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, + 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, + 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, + 20, 20, 21, 21, 21, 21, 22, 22, 22, 23, 23, + 23, 23, 24, 24, 24, 25, 25, 25, 26, 26, 26, + 27, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, + 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, + 34, 35, 35, 35, 36, 36, 36, 37, 37, 38, 38, + 38, 39, 39, 39, 40, 40, 41, 41, 41, 42, 42, + 43, 43, 44, 44, 44, 45, 45, 46, 46, 46, 47, + 47, 48, 48, 49, 49, 49, 50, 50, 51, 51, 52, + 52, 53, 53, 54, 54, 54, 55, 55, 56, 56, 57, + 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, + 63, 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, + 68, 69, 69, 70, 70, 71, 71, 72, 72, 73, 73, + 74, 74, 75, 76, 76, 77, 77, 78, 78, 79, 79, + 80, 81, 81, 82, 82, 83, 83, 84, 84, 85, 86, + 86, 87, 87, 88, 89, 89, 90, 90, 91, 92, 92, + 93, 93, 94, 95, 95, 96, 96, 97, 98, 98, 99, + 99, 100, 101, 101, 102, 103, 103, 104, 105, 105, 106, + 106, 107, 108, 108, 109, 110, 110, 111, 112, 112, 113, + 114, 114, 115, 116, 116, 117, 118, 118, 119, 120, 120, + 121, 122, 122, 123, 124, 125, 125, 126, 127, 127, 128, + 129, 130, 130, 131, 132, 132, 133, 134, 135, 135, 136, + 137, 137, 138, 139, 140, 140, 141, 142, 143, 143, 144, + 145, 146, 146, 147, 148, 149, 149, 150, 151, 152, 153, + 153, 154, 155, 156, 156, 157, 158, 159, 160, 160, 161, + 162, 163, 164, 164, 165, 166, 167, 168, 168, 169, 170, + 171, 172, 172, 173, 174, 175, 176, 177, 177, 178, 179, + 180, 181, 182, 182, 183, 184, 185, 186, 187, 188, 188, + 189, 190, 191, 192, 193, 194, 194, 195, 196, 197, 198, + 199, 200, 201, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 221, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, 318, 319, + 320, 321, 322, 323, 324, 325, 327, 328, 329, 330, 331, + 332, 333, 335, 336, 337, 338, 339, 340, 341, 343, 344, + 345, 346, 347, 348, 350, 351, 352, 353, 354, 355, 357, + 358, 359, 360, 361, 363, 364, 365, 366, 367, 369, 370, + 371, 372, 373, 375, 376, 377, 378, 379, 381, 382, 383, + 384, 386, 387, 388, 389, 390, 392, 393, 394, 395, 397, + 398, 399, 400, 402, 403, 404, 405, 407, 408, 409, 410, + 412, 413, 414, 416, 417, 418, 419, 421, 422, 423, 425, + 426, 427, 428, 430, 431, 432, 434, 435, 436, 437, 439, + 440, 441, 443, 444, 445, 447, 448, 449, 451, 452, 453, + 455, 456, 457, 459, 460, 461, 463, 464, 465, 467, 468, + 469, 471, 472, 474, 475, 476, 478, 479, 480, 482, 483, + 484, 486, 487, 489, 490, 491, 493, 494, 496, 497, 498, + 500, 501, 503, 504, 505, 507, 508, 510, 511, 512, 514, + 515, 517, 518, 519, 521, 522, 524, 525, 527, 528, 530, + 531, 532, 534, 535, 537, 538, 540, 541, 543, 544, 545, + 547, 548, 550, 551, 553, 554, 556, 557, 559, 560, 562, + 563, 565, 566, 568, 569, 571, 572, 574, 575, 577, 578, + 580, 581, 583, 584, 586, 587, 589, 590, 592, 593, 595, + 596, 598, 599, 601, 602, 604, 605, 607, 609, 610, 612, + 613, 615, 616, 618, 619, 621, 622, 624, 626, 627, 629, + 630, 632, 633, 635, 637, 638, 640, 641, 643, 645, 646, + 648, 649, 651, 652, 654, 656, 657, 659, 660, 662, 664, + 665, 667, 669, 670, 672, 673, 675, 677, 678, 680, 682, + 683, 685, 686, 688, 690, 691, 693, 695, 696, 698, 700, + 701, 703, 705, 706, 708, 710, 711, 713, 715, 716, 718, + 720, 721, 723, 725, 726, 728, 730, 731, 733, 735, 736, + 738, 740, 742, 743, 745, 747, 748, 750, 752, 754, 755, + 757, 759, 760, 762, 764, 766, 767, 769, 771, 773, 774, + 776, 778, 780, 781, 783, 785, 787, 788, 790, 792, 794, + 795, 797, 799, 801, 802, 804, 806, 808, 809, 811, 813, + 815, 817, 818, 820, 822, 824, 826, 827, 829, 831, 833, + 835, 836, 838, 840, 842, 844, 845, 847, 849, 851, 853, + 855, 856, 858, 860, 862, 864, 866, 867, 869, 871, 873, + 875, 877, 878, 880, 882, 884, 886, 888, 890, 892, 893, + 895, 897, 899, 901, 903, 905, 907, 908, 910, 912, 914, + 916, 918, 920, 922, 924, 925, 927, 929, 931, 933, 935, + 937, 939, 941, 943, 945, 946, 948, 950, 952, 954, 956, + 958, 960, 962, 964, 966, 968, 970, 972, 974, 976, 978, + 979, 981, 983, 985, 987, 989, 991, 993, 995, 997, 999, + 1001, 1003, 1005, 1007, 1009, 1011, 1013, 1015, 1017, 1019, 1021, + 1023, +}; + +static const u16 xgamma10_21[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, + 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 14, + 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, + 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, + 19, 19, 20, 20, 20, 21, 21, 21, 21, 22, 22, + 22, 22, 23, 23, 23, 24, 24, 24, 24, 25, 25, + 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 29, + 29, 29, 29, 30, 30, 30, 31, 31, 31, 32, 32, + 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, + 36, 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, + 41, 41, 41, 42, 42, 43, 43, 43, 44, 44, 45, + 45, 45, 46, 46, 47, 47, 47, 48, 48, 49, 49, + 50, 50, 50, 51, 51, 52, 52, 53, 53, 54, 54, + 54, 55, 55, 56, 56, 57, 57, 58, 58, 59, 59, + 59, 60, 60, 61, 61, 62, 62, 63, 63, 64, 64, + 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, + 70, 71, 71, 72, 72, 73, 74, 74, 75, 75, 76, + 76, 77, 77, 78, 78, 79, 79, 80, 81, 81, 82, + 82, 83, 83, 84, 85, 85, 86, 86, 87, 87, 88, + 89, 89, 90, 90, 91, 91, 92, 93, 93, 94, 94, + 95, 96, 96, 97, 97, 98, 99, 99, 100, 101, 101, + 102, 102, 103, 104, 104, 105, 106, 106, 107, 108, 108, + 109, 110, 110, 111, 111, 112, 113, 113, 114, 115, 115, + 116, 117, 117, 118, 119, 120, 120, 121, 122, 122, 123, + 124, 124, 125, 126, 126, 127, 128, 129, 129, 130, 131, + 131, 132, 133, 134, 134, 135, 136, 136, 137, 138, 139, + 139, 140, 141, 142, 142, 143, 144, 145, 145, 146, 147, + 148, 148, 149, 150, 151, 152, 152, 153, 154, 155, 155, + 156, 157, 158, 159, 159, 160, 161, 162, 163, 163, 164, + 165, 166, 167, 167, 168, 169, 170, 171, 171, 172, 173, + 174, 175, 176, 176, 177, 178, 179, 180, 181, 181, 182, + 183, 184, 185, 186, 187, 187, 188, 189, 190, 191, 192, + 193, 194, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 310, 311, 312, 313, + 314, 315, 316, 317, 319, 320, 321, 322, 323, 324, 326, + 327, 328, 329, 330, 331, 332, 334, 335, 336, 337, 338, + 339, 341, 342, 343, 344, 345, 347, 348, 349, 350, 351, + 353, 354, 355, 356, 357, 359, 360, 361, 362, 363, 365, + 366, 367, 368, 370, 371, 372, 373, 375, 376, 377, 378, + 380, 381, 382, 383, 385, 386, 387, 388, 390, 391, 392, + 393, 395, 396, 397, 399, 400, 401, 402, 404, 405, 406, + 408, 409, 410, 411, 413, 414, 415, 417, 418, 419, 421, + 422, 423, 425, 426, 427, 429, 430, 431, 433, 434, 435, + 437, 438, 439, 441, 442, 443, 445, 446, 447, 449, 450, + 452, 453, 454, 456, 457, 458, 460, 461, 463, 464, 465, + 467, 468, 469, 471, 472, 474, 475, 477, 478, 479, 481, + 482, 484, 485, 486, 488, 489, 491, 492, 494, 495, 496, + 498, 499, 501, 502, 504, 505, 507, 508, 509, 511, 512, + 514, 515, 517, 518, 520, 521, 523, 524, 526, 527, 529, + 530, 532, 533, 535, 536, 538, 539, 541, 542, 544, 545, + 547, 548, 550, 551, 553, 554, 556, 557, 559, 560, 562, + 563, 565, 566, 568, 569, 571, 573, 574, 576, 577, 579, + 580, 582, 583, 585, 587, 588, 590, 591, 593, 595, 596, + 598, 599, 601, 602, 604, 606, 607, 609, 610, 612, 614, + 615, 617, 618, 620, 622, 623, 625, 627, 628, 630, 631, + 633, 635, 636, 638, 640, 641, 643, 645, 646, 648, 650, + 651, 653, 654, 656, 658, 659, 661, 663, 664, 666, 668, + 670, 671, 673, 675, 676, 678, 680, 681, 683, 685, 686, + 688, 690, 692, 693, 695, 697, 698, 700, 702, 704, 705, + 707, 709, 711, 712, 714, 716, 717, 719, 721, 723, 724, + 726, 728, 730, 732, 733, 735, 737, 739, 740, 742, 744, + 746, 747, 749, 751, 753, 755, 756, 758, 760, 762, 764, + 765, 767, 769, 771, 773, 774, 776, 778, 780, 782, 784, + 785, 787, 789, 791, 793, 795, 796, 798, 800, 802, 804, + 806, 807, 809, 811, 813, 815, 817, 819, 821, 822, 824, + 826, 828, 830, 832, 834, 836, 837, 839, 841, 843, 845, + 847, 849, 851, 853, 855, 856, 858, 860, 862, 864, 866, + 868, 870, 872, 874, 876, 878, 880, 882, 883, 885, 887, + 889, 891, 893, 895, 897, 899, 901, 903, 905, 907, 909, + 911, 913, 915, 917, 919, 921, 923, 925, 927, 929, 931, + 933, 935, 937, 939, 941, 943, 945, 947, 949, 951, 953, + 955, 957, 959, 961, 963, 965, 967, 969, 971, 973, 975, + 977, 979, 981, 984, 986, 988, 990, 992, 994, 996, 998, + 1000, 1002, 1004, 1006, 1008, 1010, 1013, 1015, 1017, 1019, 1021, + 1023, +}; + +static const u16 xgamma10_22[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, + 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, + 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16, + 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 18, 19, 19, 19, 19, 20, 20, 20, 21, 21, 21, + 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, + 24, 25, 25, 25, 25, 26, 26, 26, 27, 27, 27, + 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 31, + 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, + 35, 35, 35, 36, 36, 37, 37, 37, 38, 38, 38, + 39, 39, 39, 40, 40, 41, 41, 41, 42, 42, 43, + 43, 43, 44, 44, 44, 45, 45, 46, 46, 46, 47, + 47, 48, 48, 49, 49, 49, 50, 50, 51, 51, 52, + 52, 52, 53, 53, 54, 54, 55, 55, 55, 56, 56, + 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 61, + 62, 62, 63, 63, 64, 64, 65, 65, 66, 66, 67, + 67, 68, 68, 69, 69, 70, 70, 71, 71, 72, 72, + 73, 73, 74, 75, 75, 76, 76, 77, 77, 78, 78, + 79, 79, 80, 80, 81, 82, 82, 83, 83, 84, 84, + 85, 85, 86, 87, 87, 88, 88, 89, 89, 90, 91, + 91, 92, 92, 93, 94, 94, 95, 95, 96, 97, 97, + 98, 98, 99, 100, 100, 101, 102, 102, 103, 103, 104, + 105, 105, 106, 107, 107, 108, 109, 109, 110, 110, 111, + 112, 112, 113, 114, 114, 115, 116, 116, 117, 118, 118, + 119, 120, 121, 121, 122, 123, 123, 124, 125, 125, 126, + 127, 127, 128, 129, 130, 130, 131, 132, 132, 133, 134, + 135, 135, 136, 137, 138, 138, 139, 140, 141, 141, 142, + 143, 144, 144, 145, 146, 147, 147, 148, 149, 150, 150, + 151, 152, 153, 154, 154, 155, 156, 157, 157, 158, 159, + 160, 161, 161, 162, 163, 164, 165, 166, 166, 167, 168, + 169, 170, 170, 171, 172, 173, 174, 175, 175, 176, 177, + 178, 179, 180, 181, 181, 182, 183, 184, 185, 186, 187, + 187, 188, 189, 190, 191, 192, 193, 194, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 267, 269, 270, 271, 272, + 273, 274, 275, 276, 277, 278, 279, 280, 281, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 294, 295, 296, + 297, 298, 299, 300, 301, 303, 304, 305, 306, 307, 308, + 309, 311, 312, 313, 314, 315, 316, 317, 319, 320, 321, + 322, 323, 324, 326, 327, 328, 329, 330, 332, 333, 334, + 335, 336, 338, 339, 340, 341, 342, 344, 345, 346, 347, + 348, 350, 351, 352, 353, 355, 356, 357, 358, 360, 361, + 362, 363, 365, 366, 367, 368, 370, 371, 372, 373, 375, + 376, 377, 378, 380, 381, 382, 384, 385, 386, 387, 389, + 390, 391, 393, 394, 395, 397, 398, 399, 401, 402, 403, + 405, 406, 407, 409, 410, 411, 413, 414, 415, 417, 418, + 419, 421, 422, 423, 425, 426, 427, 429, 430, 432, 433, + 434, 436, 437, 438, 440, 441, 443, 444, 445, 447, 448, + 450, 451, 452, 454, 455, 457, 458, 459, 461, 462, 464, + 465, 467, 468, 469, 471, 472, 474, 475, 477, 478, 480, + 481, 483, 484, 485, 487, 488, 490, 491, 493, 494, 496, + 497, 499, 500, 502, 503, 505, 506, 508, 509, 511, 512, + 514, 515, 517, 518, 520, 521, 523, 524, 526, 527, 529, + 530, 532, 534, 535, 537, 538, 540, 541, 543, 544, 546, + 548, 549, 551, 552, 554, 555, 557, 559, 560, 562, 563, + 565, 567, 568, 570, 571, 573, 575, 576, 578, 579, 581, + 583, 584, 586, 587, 589, 591, 592, 594, 596, 597, 599, + 601, 602, 604, 605, 607, 609, 610, 612, 614, 615, 617, + 619, 620, 622, 624, 625, 627, 629, 631, 632, 634, 636, + 637, 639, 641, 642, 644, 646, 648, 649, 651, 653, 654, + 656, 658, 660, 661, 663, 665, 667, 668, 670, 672, 674, + 675, 677, 679, 681, 682, 684, 686, 688, 689, 691, 693, + 695, 697, 698, 700, 702, 704, 705, 707, 709, 711, 713, + 714, 716, 718, 720, 722, 724, 725, 727, 729, 731, 733, + 735, 736, 738, 740, 742, 744, 746, 747, 749, 751, 753, + 755, 757, 759, 760, 762, 764, 766, 768, 770, 772, 774, + 776, 777, 779, 781, 783, 785, 787, 789, 791, 793, 795, + 796, 798, 800, 802, 804, 806, 808, 810, 812, 814, 816, + 818, 820, 822, 824, 826, 828, 829, 831, 833, 835, 837, + 839, 841, 843, 845, 847, 849, 851, 853, 855, 857, 859, + 861, 863, 865, 867, 869, 871, 873, 875, 877, 879, 881, + 883, 885, 887, 889, 892, 894, 896, 898, 900, 902, 904, + 906, 908, 910, 912, 914, 916, 918, 920, 922, 925, 927, + 929, 931, 933, 935, 937, 939, 941, 943, 945, 948, 950, + 952, 954, 956, 958, 960, 962, 965, 967, 969, 971, 973, + 975, 977, 980, 982, 984, 986, 988, 990, 992, 995, 997, + 999, 1001, 1003, 1005, 1008, 1010, 1012, 1014, 1016, 1019, 1021, + 1023, +}; + +static const u16 xgamma10_23[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, + 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, + 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, + 13, 13, 14, 14, 14, 14, 14, 15, 15, 15, 15, + 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, + 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, + 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, + 23, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, + 27, 27, 27, 27, 28, 28, 28, 29, 29, 29, 30, + 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, + 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, + 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, 41, + 41, 42, 42, 42, 43, 43, 43, 44, 44, 45, 45, + 45, 46, 46, 47, 47, 47, 48, 48, 49, 49, 49, + 50, 50, 51, 51, 52, 52, 52, 53, 53, 54, 54, + 55, 55, 55, 56, 56, 57, 57, 58, 58, 59, 59, + 59, 60, 60, 61, 61, 62, 62, 63, 63, 64, 64, + 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, + 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, + 76, 76, 77, 77, 78, 78, 79, 80, 80, 81, 81, + 82, 82, 83, 83, 84, 85, 85, 86, 86, 87, 87, + 88, 89, 89, 90, 90, 91, 91, 92, 93, 93, 94, + 94, 95, 96, 96, 97, 97, 98, 99, 99, 100, 100, + 101, 102, 102, 103, 104, 104, 105, 106, 106, 107, 107, + 108, 109, 109, 110, 111, 111, 112, 113, 113, 114, 115, + 115, 116, 117, 117, 118, 119, 119, 120, 121, 121, 122, + 123, 124, 124, 125, 126, 126, 127, 128, 128, 129, 130, + 131, 131, 132, 133, 133, 134, 135, 136, 136, 137, 138, + 139, 139, 140, 141, 142, 142, 143, 144, 145, 145, 146, + 147, 148, 148, 149, 150, 151, 152, 152, 153, 154, 155, + 156, 156, 157, 158, 159, 160, 160, 161, 162, 163, 164, + 164, 165, 166, 167, 168, 168, 169, 170, 171, 172, 173, + 174, 174, 175, 176, 177, 178, 179, 179, 180, 181, 182, + 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 268, + 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 280, + 281, 282, 283, 284, 285, 286, 287, 288, 290, 291, 292, + 293, 294, 295, 296, 298, 299, 300, 301, 302, 303, 304, + 306, 307, 308, 309, 310, 311, 313, 314, 315, 316, 317, + 319, 320, 321, 322, 323, 325, 326, 327, 328, 329, 331, + 332, 333, 334, 335, 337, 338, 339, 340, 342, 343, 344, + 345, 347, 348, 349, 350, 352, 353, 354, 355, 357, 358, + 359, 360, 362, 363, 364, 366, 367, 368, 369, 371, 372, + 373, 375, 376, 377, 379, 380, 381, 383, 384, 385, 386, + 388, 389, 390, 392, 393, 394, 396, 397, 399, 400, 401, + 403, 404, 405, 407, 408, 409, 411, 412, 414, 415, 416, + 418, 419, 420, 422, 423, 425, 426, 427, 429, 430, 432, + 433, 435, 436, 437, 439, 440, 442, 443, 444, 446, 447, + 449, 450, 452, 453, 455, 456, 458, 459, 460, 462, 463, + 465, 466, 468, 469, 471, 472, 474, 475, 477, 478, 480, + 481, 483, 484, 486, 487, 489, 490, 492, 493, 495, 496, + 498, 499, 501, 502, 504, 506, 507, 509, 510, 512, 513, + 515, 516, 518, 520, 521, 523, 524, 526, 527, 529, 531, + 532, 534, 535, 537, 539, 540, 542, 543, 545, 547, 548, + 550, 551, 553, 555, 556, 558, 560, 561, 563, 565, 566, + 568, 569, 571, 573, 574, 576, 578, 579, 581, 583, 584, + 586, 588, 590, 591, 593, 595, 596, 598, 600, 601, 603, + 605, 606, 608, 610, 612, 613, 615, 617, 619, 620, 622, + 624, 625, 627, 629, 631, 632, 634, 636, 638, 640, 641, + 643, 645, 647, 648, 650, 652, 654, 655, 657, 659, 661, + 663, 664, 666, 668, 670, 672, 674, 675, 677, 679, 681, + 683, 684, 686, 688, 690, 692, 694, 696, 697, 699, 701, + 703, 705, 707, 709, 710, 712, 714, 716, 718, 720, 722, + 724, 725, 727, 729, 731, 733, 735, 737, 739, 741, 743, + 745, 746, 748, 750, 752, 754, 756, 758, 760, 762, 764, + 766, 768, 770, 772, 774, 776, 778, 780, 782, 784, 786, + 787, 789, 791, 793, 795, 797, 799, 801, 803, 805, 807, + 809, 811, 814, 816, 818, 820, 822, 824, 826, 828, 830, + 832, 834, 836, 838, 840, 842, 844, 846, 848, 850, 852, + 854, 857, 859, 861, 863, 865, 867, 869, 871, 873, 875, + 878, 880, 882, 884, 886, 888, 890, 892, 894, 897, 899, + 901, 903, 905, 907, 909, 912, 914, 916, 918, 920, 922, + 925, 927, 929, 931, 933, 936, 938, 940, 942, 944, 946, + 949, 951, 953, 955, 958, 960, 962, 964, 966, 969, 971, + 973, 975, 978, 980, 982, 984, 987, 989, 991, 993, 996, + 998, 1000, 1002, 1005, 1007, 1009, 1012, 1014, 1016, 1018, 1021, + 1023, +}; + +static const u16 xgamma10_24[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15, + 15, 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, + 17, 18, 18, 18, 18, 18, 19, 19, 19, 19, 20, + 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, + 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, + 26, 26, 26, 26, 27, 27, 27, 28, 28, 28, 28, + 29, 29, 29, 30, 30, 30, 31, 31, 31, 32, 32, + 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, + 36, 36, 36, 37, 37, 38, 38, 38, 39, 39, 39, + 40, 40, 40, 41, 41, 41, 42, 42, 43, 43, 43, + 44, 44, 44, 45, 45, 46, 46, 46, 47, 47, 48, + 48, 48, 49, 49, 50, 50, 50, 51, 51, 52, 52, + 53, 53, 53, 54, 54, 55, 55, 56, 56, 56, 57, + 57, 58, 58, 59, 59, 60, 60, 61, 61, 61, 62, + 62, 63, 63, 64, 64, 65, 65, 66, 66, 67, 67, + 68, 68, 69, 69, 70, 70, 71, 71, 72, 72, 73, + 73, 74, 74, 75, 75, 76, 76, 77, 77, 78, 79, + 79, 80, 80, 81, 81, 82, 82, 83, 83, 84, 85, + 85, 86, 86, 87, 87, 88, 89, 89, 90, 90, 91, + 91, 92, 93, 93, 94, 94, 95, 96, 96, 97, 97, + 98, 99, 99, 100, 100, 101, 102, 102, 103, 104, 104, + 105, 106, 106, 107, 107, 108, 109, 109, 110, 111, 111, + 112, 113, 113, 114, 115, 115, 116, 117, 117, 118, 119, + 119, 120, 121, 121, 122, 123, 124, 124, 125, 126, 126, + 127, 128, 129, 129, 130, 131, 131, 132, 133, 134, 134, + 135, 136, 137, 137, 138, 139, 140, 140, 141, 142, 143, + 143, 144, 145, 146, 146, 147, 148, 149, 149, 150, 151, + 152, 153, 153, 154, 155, 156, 157, 157, 158, 159, 160, + 161, 161, 162, 163, 164, 165, 166, 166, 167, 168, 169, + 170, 171, 171, 172, 173, 174, 175, 176, 177, 177, 178, + 179, 180, 181, 182, 183, 184, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 279, 280, 281, 282, 283, 284, 285, 287, 288, 289, + 290, 291, 292, 293, 295, 296, 297, 298, 299, 300, 302, + 303, 304, 305, 306, 308, 309, 310, 311, 312, 314, 315, + 316, 317, 318, 320, 321, 322, 323, 324, 326, 327, 328, + 329, 331, 332, 333, 334, 336, 337, 338, 339, 341, 342, + 343, 344, 346, 347, 348, 350, 351, 352, 353, 355, 356, + 357, 359, 360, 361, 363, 364, 365, 367, 368, 369, 370, + 372, 373, 374, 376, 377, 378, 380, 381, 383, 384, 385, + 387, 388, 389, 391, 392, 393, 395, 396, 398, 399, 400, + 402, 403, 405, 406, 407, 409, 410, 412, 413, 414, 416, + 417, 419, 420, 421, 423, 424, 426, 427, 429, 430, 432, + 433, 434, 436, 437, 439, 440, 442, 443, 445, 446, 448, + 449, 451, 452, 454, 455, 457, 458, 460, 461, 463, 464, + 466, 467, 469, 470, 472, 473, 475, 476, 478, 479, 481, + 483, 484, 486, 487, 489, 490, 492, 493, 495, 497, 498, + 500, 501, 503, 505, 506, 508, 509, 511, 512, 514, 516, + 517, 519, 521, 522, 524, 525, 527, 529, 530, 532, 534, + 535, 537, 539, 540, 542, 543, 545, 547, 548, 550, 552, + 553, 555, 557, 559, 560, 562, 564, 565, 567, 569, 570, + 572, 574, 576, 577, 579, 581, 582, 584, 586, 588, 589, + 591, 593, 595, 596, 598, 600, 602, 603, 605, 607, 609, + 610, 612, 614, 616, 618, 619, 621, 623, 625, 627, 628, + 630, 632, 634, 636, 637, 639, 641, 643, 645, 647, 648, + 650, 652, 654, 656, 658, 660, 661, 663, 665, 667, 669, + 671, 673, 674, 676, 678, 680, 682, 684, 686, 688, 690, + 692, 693, 695, 697, 699, 701, 703, 705, 707, 709, 711, + 713, 715, 717, 719, 721, 723, 724, 726, 728, 730, 732, + 734, 736, 738, 740, 742, 744, 746, 748, 750, 752, 754, + 756, 758, 760, 762, 764, 766, 768, 770, 772, 774, 777, + 779, 781, 783, 785, 787, 789, 791, 793, 795, 797, 799, + 801, 803, 805, 808, 810, 812, 814, 816, 818, 820, 822, + 824, 826, 829, 831, 833, 835, 837, 839, 841, 844, 846, + 848, 850, 852, 854, 856, 859, 861, 863, 865, 867, 870, + 872, 874, 876, 878, 880, 883, 885, 887, 889, 891, 894, + 896, 898, 900, 903, 905, 907, 909, 912, 914, 916, 918, + 921, 923, 925, 927, 930, 932, 934, 936, 939, 941, 943, + 946, 948, 950, 952, 955, 957, 959, 962, 964, 966, 969, + 971, 973, 976, 978, 980, 983, 985, 987, 990, 992, 994, + 997, 999, 1002, 1004, 1006, 1009, 1011, 1013, 1016, 1018, 1021, + 1023, +}; + +static const u16 xgamma10_25[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 11, + 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17, + 17, 17, 17, 18, 18, 18, 18, 18, 19, 19, 19, + 19, 20, 20, 20, 20, 20, 21, 21, 21, 21, 22, + 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 25, + 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 28, + 28, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, + 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, + 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, + 38, 39, 39, 39, 40, 40, 40, 41, 41, 42, 42, + 42, 43, 43, 43, 44, 44, 45, 45, 45, 46, 46, + 46, 47, 47, 48, 48, 48, 49, 49, 50, 50, 50, + 51, 51, 52, 52, 53, 53, 53, 54, 54, 55, 55, + 56, 56, 56, 57, 57, 58, 58, 59, 59, 60, 60, + 60, 61, 61, 62, 62, 63, 63, 64, 64, 65, 65, + 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, + 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, + 77, 77, 78, 78, 79, 79, 80, 80, 81, 82, 82, + 83, 83, 84, 84, 85, 85, 86, 87, 87, 88, 88, + 89, 89, 90, 91, 91, 92, 92, 93, 94, 94, 95, + 95, 96, 97, 97, 98, 98, 99, 100, 100, 101, 102, + 102, 103, 103, 104, 105, 105, 106, 107, 107, 108, 109, + 109, 110, 110, 111, 112, 112, 113, 114, 114, 115, 116, + 117, 117, 118, 119, 119, 120, 121, 121, 122, 123, 123, + 124, 125, 126, 126, 127, 128, 128, 129, 130, 131, 131, + 132, 133, 133, 134, 135, 136, 136, 137, 138, 139, 139, + 140, 141, 142, 143, 143, 144, 145, 146, 146, 147, 148, + 149, 149, 150, 151, 152, 153, 153, 154, 155, 156, 157, + 158, 158, 159, 160, 161, 162, 162, 163, 164, 165, 166, + 167, 167, 168, 169, 170, 171, 172, 173, 173, 174, 175, + 176, 177, 178, 179, 180, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 247, 248, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 270, 271, 272, 273, 274, + 275, 276, 277, 279, 280, 281, 282, 283, 284, 286, 287, + 288, 289, 290, 291, 293, 294, 295, 296, 297, 298, 300, + 301, 302, 303, 304, 306, 307, 308, 309, 311, 312, 313, + 314, 315, 317, 318, 319, 320, 322, 323, 324, 325, 327, + 328, 329, 330, 332, 333, 334, 336, 337, 338, 339, 341, + 342, 343, 345, 346, 347, 349, 350, 351, 352, 354, 355, + 356, 358, 359, 360, 362, 363, 364, 366, 367, 369, 370, + 371, 373, 374, 375, 377, 378, 379, 381, 382, 384, 385, + 386, 388, 389, 391, 392, 393, 395, 396, 398, 399, 400, + 402, 403, 405, 406, 408, 409, 411, 412, 413, 415, 416, + 418, 419, 421, 422, 424, 425, 427, 428, 430, 431, 433, + 434, 436, 437, 439, 440, 442, 443, 445, 446, 448, 449, + 451, 452, 454, 455, 457, 458, 460, 461, 463, 465, 466, + 468, 469, 471, 472, 474, 476, 477, 479, 480, 482, 483, + 485, 487, 488, 490, 491, 493, 495, 496, 498, 500, 501, + 503, 504, 506, 508, 509, 511, 513, 514, 516, 518, 519, + 521, 523, 524, 526, 528, 529, 531, 533, 534, 536, 538, + 540, 541, 543, 545, 546, 548, 550, 552, 553, 555, 557, + 558, 560, 562, 564, 565, 567, 569, 571, 572, 574, 576, + 578, 580, 581, 583, 585, 587, 588, 590, 592, 594, 596, + 597, 599, 601, 603, 605, 607, 608, 610, 612, 614, 616, + 618, 619, 621, 623, 625, 627, 629, 631, 632, 634, 636, + 638, 640, 642, 644, 646, 648, 649, 651, 653, 655, 657, + 659, 661, 663, 665, 667, 669, 671, 673, 674, 676, 678, + 680, 682, 684, 686, 688, 690, 692, 694, 696, 698, 700, + 702, 704, 706, 708, 710, 712, 714, 716, 718, 720, 722, + 724, 726, 728, 730, 732, 734, 736, 739, 741, 743, 745, + 747, 749, 751, 753, 755, 757, 759, 761, 763, 766, 768, + 770, 772, 774, 776, 778, 780, 782, 785, 787, 789, 791, + 793, 795, 797, 800, 802, 804, 806, 808, 810, 813, 815, + 817, 819, 821, 824, 826, 828, 830, 832, 835, 837, 839, + 841, 843, 846, 848, 850, 852, 855, 857, 859, 861, 864, + 866, 868, 870, 873, 875, 877, 880, 882, 884, 886, 889, + 891, 893, 896, 898, 900, 903, 905, 907, 910, 912, 914, + 917, 919, 921, 924, 926, 928, 931, 933, 935, 938, 940, + 942, 945, 947, 950, 952, 954, 957, 959, 962, 964, 966, + 969, 971, 974, 976, 979, 981, 983, 986, 988, 991, 993, + 996, 998, 1001, 1003, 1006, 1008, 1011, 1013, 1016, 1018, 1021, + 1023, +}; + +static const u16 xgamma10_26[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, + 12, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, + 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, 16, + 16, 17, 17, 17, 17, 18, 18, 18, 18, 18, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, + 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, + 24, 24, 25, 25, 25, 25, 26, 26, 26, 27, 27, + 27, 27, 28, 28, 28, 28, 29, 29, 29, 30, 30, + 30, 31, 31, 31, 31, 32, 32, 32, 33, 33, 33, + 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, + 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, 41, + 41, 41, 42, 42, 43, 43, 43, 44, 44, 44, 45, + 45, 46, 46, 46, 47, 47, 47, 48, 48, 49, 49, + 49, 50, 50, 51, 51, 51, 52, 52, 53, 53, 54, + 54, 54, 55, 55, 56, 56, 57, 57, 57, 58, 58, + 59, 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, + 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, + 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, + 75, 75, 76, 76, 77, 77, 78, 78, 79, 80, 80, + 81, 81, 82, 82, 83, 83, 84, 84, 85, 86, 86, + 87, 87, 88, 88, 89, 90, 90, 91, 91, 92, 93, + 93, 94, 94, 95, 96, 96, 97, 97, 98, 99, 99, + 100, 100, 101, 102, 102, 103, 104, 104, 105, 106, 106, + 107, 107, 108, 109, 109, 110, 111, 111, 112, 113, 113, + 114, 115, 115, 116, 117, 117, 118, 119, 120, 120, 121, + 122, 122, 123, 124, 124, 125, 126, 127, 127, 128, 129, + 129, 130, 131, 132, 132, 133, 134, 135, 135, 136, 137, + 138, 138, 139, 140, 141, 141, 142, 143, 144, 145, 145, + 146, 147, 148, 149, 149, 150, 151, 152, 153, 153, 154, + 155, 156, 157, 157, 158, 159, 160, 161, 162, 162, 163, + 164, 165, 166, 167, 167, 168, 169, 170, 171, 172, 173, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 254, 256, 257, 258, 259, 260, + 261, 262, 263, 264, 266, 267, 268, 269, 270, 271, 272, + 274, 275, 276, 277, 278, 279, 281, 282, 283, 284, 285, + 286, 288, 289, 290, 291, 292, 294, 295, 296, 297, 299, + 300, 301, 302, 303, 305, 306, 307, 308, 310, 311, 312, + 313, 315, 316, 317, 318, 320, 321, 322, 323, 325, 326, + 327, 329, 330, 331, 333, 334, 335, 336, 338, 339, 340, + 342, 343, 344, 346, 347, 348, 350, 351, 352, 354, 355, + 356, 358, 359, 361, 362, 363, 365, 366, 367, 369, 370, + 372, 373, 374, 376, 377, 379, 380, 381, 383, 384, 386, + 387, 389, 390, 391, 393, 394, 396, 397, 399, 400, 402, + 403, 405, 406, 407, 409, 410, 412, 413, 415, 416, 418, + 419, 421, 422, 424, 425, 427, 428, 430, 432, 433, 435, + 436, 438, 439, 441, 442, 444, 445, 447, 449, 450, 452, + 453, 455, 456, 458, 460, 461, 463, 464, 466, 468, 469, + 471, 472, 474, 476, 477, 479, 481, 482, 484, 485, 487, + 489, 490, 492, 494, 495, 497, 499, 500, 502, 504, 505, + 507, 509, 510, 512, 514, 516, 517, 519, 521, 522, 524, + 526, 528, 529, 531, 533, 535, 536, 538, 540, 542, 543, + 545, 547, 549, 550, 552, 554, 556, 558, 559, 561, 563, + 565, 567, 568, 570, 572, 574, 576, 577, 579, 581, 583, + 585, 587, 588, 590, 592, 594, 596, 598, 600, 601, 603, + 605, 607, 609, 611, 613, 615, 617, 619, 620, 622, 624, + 626, 628, 630, 632, 634, 636, 638, 640, 642, 644, 646, + 648, 650, 651, 653, 655, 657, 659, 661, 663, 665, 667, + 669, 671, 673, 675, 677, 679, 681, 683, 685, 688, 690, + 692, 694, 696, 698, 700, 702, 704, 706, 708, 710, 712, + 714, 716, 718, 721, 723, 725, 727, 729, 731, 733, 735, + 737, 740, 742, 744, 746, 748, 750, 752, 755, 757, 759, + 761, 763, 765, 768, 770, 772, 774, 776, 779, 781, 783, + 785, 787, 790, 792, 794, 796, 798, 801, 803, 805, 807, + 810, 812, 814, 816, 819, 821, 823, 826, 828, 830, 832, + 835, 837, 839, 842, 844, 846, 849, 851, 853, 855, 858, + 860, 862, 865, 867, 870, 872, 874, 877, 879, 881, 884, + 886, 888, 891, 893, 896, 898, 900, 903, 905, 908, 910, + 913, 915, 917, 920, 922, 925, 927, 930, 932, 934, 937, + 939, 942, 944, 947, 949, 952, 954, 957, 959, 962, 964, + 967, 969, 972, 974, 977, 979, 982, 984, 987, 990, 992, + 995, 997, 1000, 1002, 1005, 1007, 1010, 1013, 1015, 1018, 1020, + 1023, +}; + +static const u16 xgamma10_27[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, + 12, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14, + 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, + 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, + 18, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, + 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, + 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, + 26, 27, 27, 27, 27, 28, 28, 28, 29, 29, 29, + 29, 30, 30, 30, 31, 31, 31, 32, 32, 32, 32, + 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, + 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 40, + 40, 40, 41, 41, 41, 42, 42, 43, 43, 43, 44, + 44, 44, 45, 45, 46, 46, 46, 47, 47, 47, 48, + 48, 49, 49, 49, 50, 50, 51, 51, 51, 52, 52, + 53, 53, 54, 54, 54, 55, 55, 56, 56, 57, 57, + 57, 58, 58, 59, 59, 60, 60, 61, 61, 61, 62, + 62, 63, 63, 64, 64, 65, 65, 66, 66, 67, 67, + 68, 68, 69, 69, 70, 70, 71, 71, 72, 72, 73, + 73, 74, 74, 75, 75, 76, 76, 77, 77, 78, 78, + 79, 79, 80, 81, 81, 82, 82, 83, 83, 84, 84, + 85, 86, 86, 87, 87, 88, 88, 89, 90, 90, 91, + 91, 92, 92, 93, 94, 94, 95, 95, 96, 97, 97, + 98, 99, 99, 100, 100, 101, 102, 102, 103, 104, 104, + 105, 105, 106, 107, 107, 108, 109, 109, 110, 111, 111, + 112, 113, 113, 114, 115, 115, 116, 117, 118, 118, 119, + 120, 120, 121, 122, 122, 123, 124, 125, 125, 126, 127, + 127, 128, 129, 130, 130, 131, 132, 133, 133, 134, 135, + 136, 136, 137, 138, 139, 139, 140, 141, 142, 143, 143, + 144, 145, 146, 146, 147, 148, 149, 150, 150, 151, 152, + 153, 154, 155, 155, 156, 157, 158, 159, 160, 160, 161, + 162, 163, 164, 165, 165, 166, 167, 168, 169, 170, 171, + 172, 172, 173, 174, 175, 176, 177, 178, 179, 180, 180, + 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 256, 257, 258, 259, + 260, 261, 262, 263, 265, 266, 267, 268, 269, 270, 272, + 273, 274, 275, 276, 278, 279, 280, 281, 282, 283, 285, + 286, 287, 288, 290, 291, 292, 293, 294, 296, 297, 298, + 299, 301, 302, 303, 304, 306, 307, 308, 309, 311, 312, + 313, 315, 316, 317, 318, 320, 321, 322, 324, 325, 326, + 328, 329, 330, 332, 333, 334, 336, 337, 338, 340, 341, + 342, 344, 345, 346, 348, 349, 351, 352, 353, 355, 356, + 357, 359, 360, 362, 363, 364, 366, 367, 369, 370, 372, + 373, 374, 376, 377, 379, 380, 382, 383, 385, 386, 387, + 389, 390, 392, 393, 395, 396, 398, 399, 401, 402, 404, + 405, 407, 408, 410, 411, 413, 414, 416, 417, 419, 421, + 422, 424, 425, 427, 428, 430, 431, 433, 435, 436, 438, + 439, 441, 442, 444, 446, 447, 449, 450, 452, 454, 455, + 457, 459, 460, 462, 463, 465, 467, 468, 470, 472, 473, + 475, 477, 478, 480, 482, 483, 485, 487, 488, 490, 492, + 494, 495, 497, 499, 500, 502, 504, 506, 507, 509, 511, + 513, 514, 516, 518, 520, 521, 523, 525, 527, 528, 530, + 532, 534, 536, 537, 539, 541, 543, 545, 546, 548, 550, + 552, 554, 556, 557, 559, 561, 563, 565, 567, 569, 570, + 572, 574, 576, 578, 580, 582, 584, 586, 587, 589, 591, + 593, 595, 597, 599, 601, 603, 605, 607, 609, 611, 613, + 614, 616, 618, 620, 622, 624, 626, 628, 630, 632, 634, + 636, 638, 640, 642, 644, 646, 648, 650, 652, 654, 656, + 659, 661, 663, 665, 667, 669, 671, 673, 675, 677, 679, + 681, 683, 685, 688, 690, 692, 694, 696, 698, 700, 702, + 705, 707, 709, 711, 713, 715, 717, 720, 722, 724, 726, + 728, 730, 733, 735, 737, 739, 741, 744, 746, 748, 750, + 752, 755, 757, 759, 761, 764, 766, 768, 770, 773, 775, + 777, 779, 782, 784, 786, 789, 791, 793, 795, 798, 800, + 802, 805, 807, 809, 812, 814, 816, 819, 821, 823, 826, + 828, 831, 833, 835, 838, 840, 842, 845, 847, 850, 852, + 854, 857, 859, 862, 864, 867, 869, 871, 874, 876, 879, + 881, 884, 886, 889, 891, 894, 896, 899, 901, 903, 906, + 908, 911, 914, 916, 919, 921, 924, 926, 929, 931, 934, + 936, 939, 941, 944, 947, 949, 952, 954, 957, 959, 962, + 965, 967, 970, 973, 975, 978, 980, 983, 986, 988, 991, + 994, 996, 999, 1002, 1004, 1007, 1010, 1012, 1015, 1018, 1020, + 1023, +}; + +static const u16 xgamma10_28[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, + 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, + 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 14, + 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 16, + 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, 18, + 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, + 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, + 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, + 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 29, + 29, 29, 29, 30, 30, 30, 31, 31, 31, 31, 32, + 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, + 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, + 39, 40, 40, 40, 41, 41, 41, 42, 42, 42, 43, + 43, 43, 44, 44, 45, 45, 45, 46, 46, 46, 47, + 47, 48, 48, 48, 49, 49, 50, 50, 50, 51, 51, + 52, 52, 52, 53, 53, 54, 54, 55, 55, 55, 56, + 56, 57, 57, 58, 58, 58, 59, 59, 60, 60, 61, + 61, 62, 62, 63, 63, 63, 64, 64, 65, 65, 66, + 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71, + 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, 77, + 77, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, + 83, 84, 85, 85, 86, 86, 87, 87, 88, 89, 89, + 90, 90, 91, 92, 92, 93, 93, 94, 95, 95, 96, + 96, 97, 98, 98, 99, 99, 100, 101, 101, 102, 103, + 103, 104, 105, 105, 106, 106, 107, 108, 108, 109, 110, + 110, 111, 112, 112, 113, 114, 115, 115, 116, 117, 117, + 118, 119, 119, 120, 121, 122, 122, 123, 124, 124, 125, + 126, 127, 127, 128, 129, 130, 130, 131, 132, 132, 133, + 134, 135, 136, 136, 137, 138, 139, 139, 140, 141, 142, + 143, 143, 144, 145, 146, 146, 147, 148, 149, 150, 151, + 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 160, + 161, 161, 162, 163, 164, 165, 166, 167, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 251, 252, 253, 254, 255, 256, 257, 259, + 260, 261, 262, 263, 264, 266, 267, 268, 269, 270, 272, + 273, 274, 275, 276, 278, 279, 280, 281, 282, 284, 285, + 286, 287, 289, 290, 291, 292, 294, 295, 296, 297, 299, + 300, 301, 302, 304, 305, 306, 308, 309, 310, 311, 313, + 314, 315, 317, 318, 319, 321, 322, 323, 325, 326, 327, + 329, 330, 331, 333, 334, 336, 337, 338, 340, 341, 342, + 344, 345, 347, 348, 349, 351, 352, 354, 355, 356, 358, + 359, 361, 362, 364, 365, 366, 368, 369, 371, 372, 374, + 375, 377, 378, 380, 381, 383, 384, 386, 387, 389, 390, + 392, 393, 395, 396, 398, 399, 401, 402, 404, 405, 407, + 408, 410, 412, 413, 415, 416, 418, 419, 421, 423, 424, + 426, 427, 429, 431, 432, 434, 435, 437, 439, 440, 442, + 444, 445, 447, 448, 450, 452, 453, 455, 457, 458, 460, + 462, 463, 465, 467, 468, 470, 472, 474, 475, 477, 479, + 480, 482, 484, 486, 487, 489, 491, 493, 494, 496, 498, + 500, 501, 503, 505, 507, 509, 510, 512, 514, 516, 518, + 519, 521, 523, 525, 527, 528, 530, 532, 534, 536, 538, + 539, 541, 543, 545, 547, 549, 551, 553, 554, 556, 558, + 560, 562, 564, 566, 568, 570, 572, 574, 575, 577, 579, + 581, 583, 585, 587, 589, 591, 593, 595, 597, 599, 601, + 603, 605, 607, 609, 611, 613, 615, 617, 619, 621, 623, + 625, 627, 629, 631, 633, 635, 637, 640, 642, 644, 646, + 648, 650, 652, 654, 656, 658, 660, 663, 665, 667, 669, + 671, 673, 675, 678, 680, 682, 684, 686, 688, 690, 693, + 695, 697, 699, 701, 704, 706, 708, 710, 712, 715, 717, + 719, 721, 724, 726, 728, 730, 733, 735, 737, 739, 742, + 744, 746, 749, 751, 753, 755, 758, 760, 762, 765, 767, + 769, 772, 774, 776, 779, 781, 783, 786, 788, 790, 793, + 795, 798, 800, 802, 805, 807, 810, 812, 814, 817, 819, + 822, 824, 827, 829, 831, 834, 836, 839, 841, 844, 846, + 849, 851, 854, 856, 859, 861, 864, 866, 869, 871, 874, + 876, 879, 881, 884, 887, 889, 892, 894, 897, 899, 902, + 905, 907, 910, 912, 915, 918, 920, 923, 925, 928, 931, + 933, 936, 939, 941, 944, 947, 949, 952, 955, 957, 960, + 963, 965, 968, 971, 973, 976, 979, 982, 984, 987, 990, + 992, 995, 998, 1001, 1004, 1006, 1009, 1012, 1015, 1017, 1020, + 1023, +}; + +static const u16 xgamma10_29[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, + 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, + 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, + 18, 18, 18, 18, 19, 19, 19, 19, 19, 20, 20, + 20, 20, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, + 25, 26, 26, 26, 26, 27, 27, 27, 28, 28, 28, + 28, 29, 29, 29, 29, 30, 30, 30, 31, 31, 31, + 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, + 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, + 38, 39, 39, 39, 40, 40, 41, 41, 41, 42, 42, + 42, 43, 43, 43, 44, 44, 44, 45, 45, 46, 46, + 46, 47, 47, 48, 48, 48, 49, 49, 49, 50, 50, + 51, 51, 52, 52, 52, 53, 53, 54, 54, 54, 55, + 55, 56, 56, 57, 57, 57, 58, 58, 59, 59, 60, + 60, 61, 61, 61, 62, 62, 63, 63, 64, 64, 65, + 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, + 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, + 76, 77, 77, 78, 78, 79, 80, 80, 81, 81, 82, + 82, 83, 83, 84, 85, 85, 86, 86, 87, 87, 88, + 89, 89, 90, 90, 91, 92, 92, 93, 93, 94, 95, + 95, 96, 96, 97, 98, 98, 99, 99, 100, 101, 101, + 102, 103, 103, 104, 105, 105, 106, 107, 107, 108, 109, + 109, 110, 111, 111, 112, 113, 113, 114, 115, 115, 116, + 117, 117, 118, 119, 120, 120, 121, 122, 122, 123, 124, + 125, 125, 126, 127, 128, 128, 129, 130, 131, 131, 132, + 133, 134, 134, 135, 136, 137, 137, 138, 139, 140, 141, + 141, 142, 143, 144, 145, 145, 146, 147, 148, 149, 149, + 150, 151, 152, 153, 154, 154, 155, 156, 157, 158, 159, + 160, 160, 161, 162, 163, 164, 165, 166, 166, 167, 168, + 169, 170, 171, 172, 173, 174, 175, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 229, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 241, 242, 243, 244, 245, 246, + 247, 248, 250, 251, 252, 253, 254, 255, 257, 258, 259, + 260, 261, 263, 264, 265, 266, 267, 269, 270, 271, 272, + 273, 275, 276, 277, 278, 280, 281, 282, 283, 285, 286, + 287, 288, 290, 291, 292, 293, 295, 296, 297, 299, 300, + 301, 302, 304, 305, 306, 308, 309, 310, 312, 313, 314, + 316, 317, 318, 320, 321, 322, 324, 325, 327, 328, 329, + 331, 332, 333, 335, 336, 338, 339, 340, 342, 343, 345, + 346, 348, 349, 350, 352, 353, 355, 356, 358, 359, 361, + 362, 363, 365, 366, 368, 369, 371, 372, 374, 375, 377, + 378, 380, 381, 383, 384, 386, 388, 389, 391, 392, 394, + 395, 397, 398, 400, 402, 403, 405, 406, 408, 409, 411, + 413, 414, 416, 417, 419, 421, 422, 424, 426, 427, 429, + 430, 432, 434, 435, 437, 439, 440, 442, 444, 445, 447, + 449, 450, 452, 454, 456, 457, 459, 461, 462, 464, 466, + 468, 469, 471, 473, 475, 476, 478, 480, 482, 483, 485, + 487, 489, 491, 492, 494, 496, 498, 500, 501, 503, 505, + 507, 509, 511, 512, 514, 516, 518, 520, 522, 524, 525, + 527, 529, 531, 533, 535, 537, 539, 541, 542, 544, 546, + 548, 550, 552, 554, 556, 558, 560, 562, 564, 566, 568, + 570, 572, 574, 576, 578, 580, 582, 584, 586, 588, 590, + 592, 594, 596, 598, 600, 602, 604, 606, 608, 610, 612, + 614, 616, 618, 621, 623, 625, 627, 629, 631, 633, 635, + 637, 640, 642, 644, 646, 648, 650, 652, 655, 657, 659, + 661, 663, 665, 668, 670, 672, 674, 676, 679, 681, 683, + 685, 688, 690, 692, 694, 697, 699, 701, 703, 706, 708, + 710, 712, 715, 717, 719, 722, 724, 726, 729, 731, 733, + 736, 738, 740, 743, 745, 747, 750, 752, 754, 757, 759, + 762, 764, 766, 769, 771, 774, 776, 778, 781, 783, 786, + 788, 791, 793, 795, 798, 800, 803, 805, 808, 810, 813, + 815, 818, 820, 823, 825, 828, 830, 833, 835, 838, 841, + 843, 846, 848, 851, 853, 856, 859, 861, 864, 866, 869, + 872, 874, 877, 879, 882, 885, 887, 890, 893, 895, 898, + 901, 903, 906, 909, 911, 914, 917, 919, 922, 925, 927, + 930, 933, 936, 938, 941, 944, 947, 949, 952, 955, 958, + 960, 963, 966, 969, 972, 974, 977, 980, 983, 986, 989, + 991, 994, 997, 1000, 1003, 1006, 1009, 1011, 1014, 1017, 1020, + 1023, +}; + +static const u16 xgamma10_30[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, + 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, + 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, + 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, + 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 20, + 20, 20, 20, 21, 21, 21, 21, 21, 22, 22, 22, + 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, + 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, + 28, 28, 28, 29, 29, 29, 30, 30, 30, 30, 31, + 31, 31, 32, 32, 32, 33, 33, 33, 33, 34, 34, + 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, + 38, 38, 39, 39, 39, 40, 40, 40, 41, 41, 41, + 42, 42, 42, 43, 43, 43, 44, 44, 45, 45, 45, + 46, 46, 46, 47, 47, 48, 48, 48, 49, 49, 50, + 50, 50, 51, 51, 52, 52, 52, 53, 53, 54, 54, + 55, 55, 55, 56, 56, 57, 57, 58, 58, 58, 59, + 59, 60, 60, 61, 61, 62, 62, 63, 63, 63, 64, + 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, + 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, + 75, 76, 77, 77, 78, 78, 79, 79, 80, 80, 81, + 81, 82, 83, 83, 84, 84, 85, 85, 86, 86, 87, + 88, 88, 89, 89, 90, 91, 91, 92, 92, 93, 94, + 94, 95, 95, 96, 97, 97, 98, 99, 99, 100, 100, + 101, 102, 102, 103, 104, 104, 105, 106, 106, 107, 108, + 108, 109, 110, 110, 111, 112, 112, 113, 114, 114, 115, + 116, 117, 117, 118, 119, 119, 120, 121, 122, 122, 123, + 124, 125, 125, 126, 127, 128, 128, 129, 130, 131, 131, + 132, 133, 134, 134, 135, 136, 137, 137, 138, 139, 140, + 141, 141, 142, 143, 144, 145, 146, 146, 147, 148, 149, + 150, 150, 151, 152, 153, 154, 155, 156, 156, 157, 158, + 159, 160, 161, 162, 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 236, 237, 238, 239, 240, 241, 242, 244, 245, 246, 247, + 248, 249, 250, 252, 253, 254, 255, 256, 258, 259, 260, + 261, 262, 264, 265, 266, 267, 269, 270, 271, 272, 273, + 275, 276, 277, 278, 280, 281, 282, 284, 285, 286, 287, + 289, 290, 291, 293, 294, 295, 296, 298, 299, 300, 302, + 303, 304, 306, 307, 308, 310, 311, 313, 314, 315, 317, + 318, 319, 321, 322, 324, 325, 326, 328, 329, 331, 332, + 333, 335, 336, 338, 339, 341, 342, 343, 345, 346, 348, + 349, 351, 352, 354, 355, 357, 358, 360, 361, 363, 364, + 366, 367, 369, 370, 372, 373, 375, 376, 378, 379, 381, + 383, 384, 386, 387, 389, 390, 392, 394, 395, 397, 398, + 400, 402, 403, 405, 406, 408, 410, 411, 413, 415, 416, + 418, 419, 421, 423, 424, 426, 428, 429, 431, 433, 435, + 436, 438, 440, 441, 443, 445, 447, 448, 450, 452, 453, + 455, 457, 459, 460, 462, 464, 466, 468, 469, 471, 473, + 475, 477, 478, 480, 482, 484, 486, 487, 489, 491, 493, + 495, 497, 498, 500, 502, 504, 506, 508, 510, 512, 513, + 515, 517, 519, 521, 523, 525, 527, 529, 531, 533, 535, + 537, 539, 540, 542, 544, 546, 548, 550, 552, 554, 556, + 558, 560, 562, 564, 566, 568, 570, 572, 574, 577, 579, + 581, 583, 585, 587, 589, 591, 593, 595, 597, 599, 601, + 604, 606, 608, 610, 612, 614, 616, 618, 621, 623, 625, + 627, 629, 631, 634, 636, 638, 640, 642, 645, 647, 649, + 651, 653, 656, 658, 660, 662, 665, 667, 669, 671, 674, + 676, 678, 680, 683, 685, 687, 690, 692, 694, 697, 699, + 701, 704, 706, 708, 711, 713, 715, 718, 720, 722, 725, + 727, 730, 732, 734, 737, 739, 742, 744, 746, 749, 751, + 754, 756, 759, 761, 764, 766, 769, 771, 774, 776, 779, + 781, 784, 786, 789, 791, 794, 796, 799, 801, 804, 806, + 809, 812, 814, 817, 819, 822, 824, 827, 830, 832, 835, + 837, 840, 843, 845, 848, 851, 853, 856, 859, 861, 864, + 867, 869, 872, 875, 878, 880, 883, 886, 888, 891, 894, + 897, 899, 902, 905, 908, 910, 913, 916, 919, 922, 924, + 927, 930, 933, 936, 938, 941, 944, 947, 950, 953, 956, + 958, 961, 964, 967, 970, 973, 976, 979, 982, 984, 987, + 990, 993, 996, 999, 1002, 1005, 1008, 1011, 1014, 1017, 1020, + 1023, +}; + +static const u16 xgamma10_31[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, + 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, + 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, + 13, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, + 15, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, + 17, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 21, 21, 21, 21, 21, 22, 22, + 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, + 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, + 28, 28, 28, 28, 29, 29, 29, 30, 30, 30, 30, + 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 34, + 34, 34, 35, 35, 35, 36, 36, 36, 36, 37, 37, + 37, 38, 38, 38, 39, 39, 39, 40, 40, 41, 41, + 41, 42, 42, 42, 43, 43, 43, 44, 44, 44, 45, + 45, 46, 46, 46, 47, 47, 47, 48, 48, 49, 49, + 49, 50, 50, 51, 51, 51, 52, 52, 53, 53, 54, + 54, 54, 55, 55, 56, 56, 57, 57, 57, 58, 58, + 59, 59, 60, 60, 61, 61, 61, 62, 62, 63, 63, + 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, + 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, + 75, 75, 76, 76, 77, 77, 78, 79, 79, 80, 80, + 81, 81, 82, 82, 83, 84, 84, 85, 85, 86, 86, + 87, 88, 88, 89, 89, 90, 91, 91, 92, 92, 93, + 94, 94, 95, 95, 96, 97, 97, 98, 99, 99, 100, + 101, 101, 102, 102, 103, 104, 104, 105, 106, 106, 107, + 108, 108, 109, 110, 111, 111, 112, 113, 113, 114, 115, + 115, 116, 117, 118, 118, 119, 120, 120, 121, 122, 123, + 123, 124, 125, 126, 126, 127, 128, 129, 129, 130, 131, + 132, 132, 133, 134, 135, 136, 136, 137, 138, 139, 140, + 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, + 149, 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, + 159, 160, 161, 162, 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 227, 228, 229, 230, 231, 232, 233, 234, 236, + 237, 238, 239, 240, 241, 243, 244, 245, 246, 247, 248, + 250, 251, 252, 253, 254, 256, 257, 258, 259, 260, 262, + 263, 264, 265, 267, 268, 269, 270, 272, 273, 274, 275, + 277, 278, 279, 281, 282, 283, 285, 286, 287, 288, 290, + 291, 292, 294, 295, 296, 298, 299, 300, 302, 303, 305, + 306, 307, 309, 310, 311, 313, 314, 316, 317, 318, 320, + 321, 323, 324, 325, 327, 328, 330, 331, 333, 334, 336, + 337, 338, 340, 341, 343, 344, 346, 347, 349, 350, 352, + 353, 355, 356, 358, 359, 361, 362, 364, 366, 367, 369, + 370, 372, 373, 375, 376, 378, 380, 381, 383, 384, 386, + 388, 389, 391, 392, 394, 396, 397, 399, 401, 402, 404, + 406, 407, 409, 411, 412, 414, 416, 417, 419, 421, 422, + 424, 426, 427, 429, 431, 433, 434, 436, 438, 440, 441, + 443, 445, 447, 448, 450, 452, 454, 456, 457, 459, 461, + 463, 465, 466, 468, 470, 472, 474, 476, 477, 479, 481, + 483, 485, 487, 489, 490, 492, 494, 496, 498, 500, 502, + 504, 506, 508, 510, 511, 513, 515, 517, 519, 521, 523, + 525, 527, 529, 531, 533, 535, 537, 539, 541, 543, 545, + 547, 549, 551, 553, 555, 557, 559, 561, 564, 566, 568, + 570, 572, 574, 576, 578, 580, 582, 584, 587, 589, 591, + 593, 595, 597, 599, 602, 604, 606, 608, 610, 613, 615, + 617, 619, 621, 624, 626, 628, 630, 632, 635, 637, 639, + 641, 644, 646, 648, 651, 653, 655, 657, 660, 662, 664, + 667, 669, 671, 674, 676, 678, 681, 683, 685, 688, 690, + 692, 695, 697, 700, 702, 704, 707, 709, 712, 714, 717, + 719, 721, 724, 726, 729, 731, 734, 736, 739, 741, 744, + 746, 749, 751, 754, 756, 759, 761, 764, 766, 769, 772, + 774, 777, 779, 782, 784, 787, 790, 792, 795, 797, 800, + 803, 805, 808, 811, 813, 816, 819, 821, 824, 827, 829, + 832, 835, 837, 840, 843, 845, 848, 851, 854, 856, 859, + 862, 865, 867, 870, 873, 876, 879, 881, 884, 887, 890, + 893, 895, 898, 901, 904, 907, 910, 913, 915, 918, 921, + 924, 927, 930, 933, 936, 939, 942, 945, 947, 950, 953, + 956, 959, 962, 965, 968, 971, 974, 977, 980, 983, 986, + 989, 992, 995, 998, 1001, 1005, 1008, 1011, 1014, 1017, 1020, + 1023, +}; + +static const u16 xgamma10_32[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, + 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, + 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, + 13, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, + 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, + 17, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, + 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, + 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, + 27, 28, 28, 28, 28, 29, 29, 29, 30, 30, 30, + 30, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, + 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, + 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 41, + 41, 41, 42, 42, 42, 43, 43, 43, 44, 44, 44, + 45, 45, 46, 46, 46, 47, 47, 48, 48, 48, 49, + 49, 49, 50, 50, 51, 51, 51, 52, 52, 53, 53, + 54, 54, 54, 55, 55, 56, 56, 57, 57, 57, 58, + 58, 59, 59, 60, 60, 61, 61, 62, 62, 62, 63, + 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, + 69, 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, + 74, 75, 75, 76, 77, 77, 78, 78, 79, 79, 80, + 80, 81, 81, 82, 83, 83, 84, 84, 85, 85, 86, + 87, 87, 88, 88, 89, 90, 90, 91, 91, 92, 93, + 93, 94, 95, 95, 96, 96, 97, 98, 98, 99, 100, + 100, 101, 102, 102, 103, 104, 104, 105, 106, 106, 107, + 108, 108, 109, 110, 110, 111, 112, 112, 113, 114, 114, + 115, 116, 117, 117, 118, 119, 120, 120, 121, 122, 122, + 123, 124, 125, 125, 126, 127, 128, 129, 129, 130, 131, + 132, 132, 133, 134, 135, 136, 136, 137, 138, 139, 140, + 140, 141, 142, 143, 144, 145, 145, 146, 147, 148, 149, + 150, 150, 151, 152, 153, 154, 155, 156, 157, 157, 158, + 159, 160, 161, 162, 163, 164, 165, 166, 166, 167, 168, + 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, + 190, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 224, 225, + 226, 227, 228, 229, 230, 231, 233, 234, 235, 236, 237, + 238, 240, 241, 242, 243, 244, 246, 247, 248, 249, 250, + 252, 253, 254, 255, 257, 258, 259, 260, 262, 263, 264, + 265, 267, 268, 269, 270, 272, 273, 274, 276, 277, 278, + 280, 281, 282, 283, 285, 286, 287, 289, 290, 291, 293, + 294, 296, 297, 298, 300, 301, 302, 304, 305, 307, 308, + 309, 311, 312, 314, 315, 316, 318, 319, 321, 322, 324, + 325, 327, 328, 330, 331, 332, 334, 335, 337, 338, 340, + 341, 343, 344, 346, 347, 349, 351, 352, 354, 355, 357, + 358, 360, 361, 363, 364, 366, 368, 369, 371, 372, 374, + 376, 377, 379, 380, 382, 384, 385, 387, 389, 390, 392, + 394, 395, 397, 399, 400, 402, 404, 405, 407, 409, 410, + 412, 414, 416, 417, 419, 421, 423, 424, 426, 428, 430, + 431, 433, 435, 437, 438, 440, 442, 444, 446, 447, 449, + 451, 453, 455, 457, 458, 460, 462, 464, 466, 468, 469, + 471, 473, 475, 477, 479, 481, 483, 485, 487, 488, 490, + 492, 494, 496, 498, 500, 502, 504, 506, 508, 510, 512, + 514, 516, 518, 520, 522, 524, 526, 528, 530, 532, 534, + 536, 538, 540, 542, 544, 547, 549, 551, 553, 555, 557, + 559, 561, 563, 565, 568, 570, 572, 574, 576, 578, 581, + 583, 585, 587, 589, 591, 594, 596, 598, 600, 602, 605, + 607, 609, 611, 614, 616, 618, 620, 623, 625, 627, 630, + 632, 634, 636, 639, 641, 643, 646, 648, 650, 653, 655, + 657, 660, 662, 665, 667, 669, 672, 674, 677, 679, 681, + 684, 686, 689, 691, 694, 696, 698, 701, 703, 706, 708, + 711, 713, 716, 718, 721, 723, 726, 728, 731, 734, 736, + 739, 741, 744, 746, 749, 751, 754, 757, 759, 762, 765, + 767, 770, 772, 775, 778, 780, 783, 786, 788, 791, 794, + 796, 799, 802, 804, 807, 810, 813, 815, 818, 821, 824, + 826, 829, 832, 835, 838, 840, 843, 846, 849, 852, 854, + 857, 860, 863, 866, 869, 871, 874, 877, 880, 883, 886, + 889, 892, 895, 897, 900, 903, 906, 909, 912, 915, 918, + 921, 924, 927, 930, 933, 936, 939, 942, 945, 948, 951, + 954, 957, 960, 963, 967, 970, 973, 976, 979, 982, 985, + 988, 991, 994, 998, 1001, 1004, 1007, 1010, 1013, 1017, 1020, + 1023, +}; + +static const u16 xgamma10_33[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, + 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, + 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, + 13, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, + 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, + 17, 17, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 20, 20, 20, 20, 21, 21, 21, 21, 21, 22, + 22, 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, + 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, + 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, + 30, 31, 31, 31, 31, 32, 32, 32, 33, 33, 33, + 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, + 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, + 41, 41, 41, 42, 42, 42, 43, 43, 44, 44, 44, + 45, 45, 45, 46, 46, 47, 47, 47, 48, 48, 48, + 49, 49, 50, 50, 50, 51, 51, 52, 52, 53, 53, + 53, 54, 54, 55, 55, 55, 56, 56, 57, 57, 58, + 58, 59, 59, 59, 60, 60, 61, 61, 62, 62, 63, + 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, + 69, 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, + 74, 75, 75, 76, 76, 77, 77, 78, 79, 79, 80, + 80, 81, 81, 82, 82, 83, 84, 84, 85, 85, 86, + 87, 87, 88, 88, 89, 90, 90, 91, 91, 92, 93, + 93, 94, 94, 95, 96, 96, 97, 98, 98, 99, 100, + 100, 101, 102, 102, 103, 104, 104, 105, 106, 106, 107, + 108, 108, 109, 110, 110, 111, 112, 112, 113, 114, 115, + 115, 116, 117, 118, 118, 119, 120, 120, 121, 122, 123, + 123, 124, 125, 126, 127, 127, 128, 129, 130, 130, 131, + 132, 133, 134, 134, 135, 136, 137, 138, 138, 139, 140, + 141, 142, 143, 143, 144, 145, 146, 147, 148, 148, 149, + 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, + 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 218, 219, 220, 221, 222, 223, 224, 226, 227, + 228, 229, 230, 231, 233, 234, 235, 236, 237, 238, 240, + 241, 242, 243, 245, 246, 247, 248, 249, 251, 252, 253, + 254, 256, 257, 258, 259, 261, 262, 263, 265, 266, 267, + 268, 270, 271, 272, 274, 275, 276, 278, 279, 280, 282, + 283, 284, 286, 287, 288, 290, 291, 292, 294, 295, 297, + 298, 299, 301, 302, 304, 305, 307, 308, 309, 311, 312, + 314, 315, 317, 318, 320, 321, 322, 324, 325, 327, 328, + 330, 331, 333, 334, 336, 337, 339, 341, 342, 344, 345, + 347, 348, 350, 351, 353, 355, 356, 358, 359, 361, 362, + 364, 366, 367, 369, 371, 372, 374, 375, 377, 379, 380, + 382, 384, 385, 387, 389, 390, 392, 394, 395, 397, 399, + 401, 402, 404, 406, 408, 409, 411, 413, 414, 416, 418, + 420, 422, 423, 425, 427, 429, 431, 432, 434, 436, 438, + 440, 441, 443, 445, 447, 449, 451, 453, 454, 456, 458, + 460, 462, 464, 466, 468, 470, 472, 473, 475, 477, 479, + 481, 483, 485, 487, 489, 491, 493, 495, 497, 499, 501, + 503, 505, 507, 509, 511, 513, 515, 517, 519, 521, 523, + 525, 528, 530, 532, 534, 536, 538, 540, 542, 544, 547, + 549, 551, 553, 555, 557, 559, 562, 564, 566, 568, 570, + 573, 575, 577, 579, 581, 584, 586, 588, 590, 593, 595, + 597, 599, 602, 604, 606, 609, 611, 613, 615, 618, 620, + 622, 625, 627, 629, 632, 634, 637, 639, 641, 644, 646, + 648, 651, 653, 656, 658, 661, 663, 665, 668, 670, 673, + 675, 678, 680, 683, 685, 688, 690, 693, 695, 698, 700, + 703, 705, 708, 710, 713, 716, 718, 721, 723, 726, 729, + 731, 734, 736, 739, 742, 744, 747, 750, 752, 755, 758, + 760, 763, 766, 768, 771, 774, 776, 779, 782, 785, 787, + 790, 793, 796, 798, 801, 804, 807, 810, 812, 815, 818, + 821, 824, 827, 829, 832, 835, 838, 841, 844, 847, 850, + 852, 855, 858, 861, 864, 867, 870, 873, 876, 879, 882, + 885, 888, 891, 894, 897, 900, 903, 906, 909, 912, 915, + 918, 921, 924, 927, 930, 933, 937, 940, 943, 946, 949, + 952, 955, 958, 962, 965, 968, 971, 974, 978, 981, 984, + 987, 990, 994, 997, 1000, 1003, 1007, 1010, 1013, 1016, 1020, + 1023, +}; + +static const u16 xgamma10_34[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, + 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, + 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, + 13, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, + 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, + 17, 17, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 20, 20, 20, 20, 21, 21, 21, 21, 21, 22, + 22, 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, + 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, + 27, 27, 28, 28, 28, 29, 29, 29, 29, 30, 30, + 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, + 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, + 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, + 41, 41, 41, 42, 42, 42, 43, 43, 43, 44, 44, + 45, 45, 45, 46, 46, 46, 47, 47, 48, 48, 48, + 49, 49, 50, 50, 50, 51, 51, 52, 52, 52, 53, + 53, 54, 54, 55, 55, 55, 56, 56, 57, 57, 58, + 58, 59, 59, 59, 60, 60, 61, 61, 62, 62, 63, + 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, + 69, 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, + 74, 75, 75, 76, 76, 77, 78, 78, 79, 79, 80, + 80, 81, 81, 82, 83, 83, 84, 84, 85, 86, 86, + 87, 87, 88, 88, 89, 90, 90, 91, 92, 92, 93, + 93, 94, 95, 95, 96, 97, 97, 98, 99, 99, 100, + 101, 101, 102, 102, 103, 104, 105, 105, 106, 107, 107, + 108, 109, 109, 110, 111, 111, 112, 113, 114, 114, 115, + 116, 117, 117, 118, 119, 119, 120, 121, 122, 123, 123, + 124, 125, 126, 126, 127, 128, 129, 129, 130, 131, 132, + 133, 133, 134, 135, 136, 137, 138, 138, 139, 140, 141, + 142, 143, 143, 144, 145, 146, 147, 148, 149, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 207, 208, 209, 210, 211, 212, 213, 214, 215, 217, + 218, 219, 220, 221, 222, 223, 225, 226, 227, 228, 229, + 231, 232, 233, 234, 235, 237, 238, 239, 240, 241, 243, + 244, 245, 246, 248, 249, 250, 251, 253, 254, 255, 256, + 258, 259, 260, 262, 263, 264, 266, 267, 268, 269, 271, + 272, 273, 275, 276, 278, 279, 280, 282, 283, 284, 286, + 287, 288, 290, 291, 293, 294, 296, 297, 298, 300, 301, + 303, 304, 306, 307, 308, 310, 311, 313, 314, 316, 317, + 319, 320, 322, 323, 325, 326, 328, 329, 331, 332, 334, + 335, 337, 339, 340, 342, 343, 345, 346, 348, 350, 351, + 353, 354, 356, 358, 359, 361, 363, 364, 366, 367, 369, + 371, 372, 374, 376, 377, 379, 381, 383, 384, 386, 388, + 389, 391, 393, 395, 396, 398, 400, 402, 403, 405, 407, + 409, 410, 412, 414, 416, 418, 419, 421, 423, 425, 427, + 429, 430, 432, 434, 436, 438, 440, 442, 443, 445, 447, + 449, 451, 453, 455, 457, 459, 461, 463, 464, 466, 468, + 470, 472, 474, 476, 478, 480, 482, 484, 486, 488, 490, + 492, 494, 496, 498, 500, 503, 505, 507, 509, 511, 513, + 515, 517, 519, 521, 523, 526, 528, 530, 532, 534, 536, + 538, 541, 543, 545, 547, 549, 551, 554, 556, 558, 560, + 563, 565, 567, 569, 572, 574, 576, 578, 581, 583, 585, + 587, 590, 592, 594, 597, 599, 601, 604, 606, 608, 611, + 613, 615, 618, 620, 623, 625, 627, 630, 632, 635, 637, + 640, 642, 644, 647, 649, 652, 654, 657, 659, 662, 664, + 667, 669, 672, 674, 677, 679, 682, 685, 687, 690, 692, + 695, 697, 700, 703, 705, 708, 711, 713, 716, 718, 721, + 724, 726, 729, 732, 734, 737, 740, 743, 745, 748, 751, + 753, 756, 759, 762, 764, 767, 770, 773, 776, 778, 781, + 784, 787, 790, 793, 795, 798, 801, 804, 807, 810, 813, + 815, 818, 821, 824, 827, 830, 833, 836, 839, 842, 845, + 848, 851, 854, 857, 860, 863, 866, 869, 872, 875, 878, + 881, 884, 887, 890, 893, 896, 899, 903, 906, 909, 912, + 915, 918, 921, 925, 928, 931, 934, 937, 940, 944, 947, + 950, 953, 957, 960, 963, 966, 970, 973, 976, 979, 983, + 986, 989, 993, 996, 999, 1003, 1006, 1009, 1013, 1016, 1020, + 1023, +}; + +static const u16 xgamma10_35[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, + 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, + 13, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, + 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, + 17, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 22, + 22, 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, + 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, + 27, 27, 28, 28, 28, 29, 29, 29, 29, 30, 30, + 30, 31, 31, 31, 31, 32, 32, 32, 33, 33, 33, + 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, + 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, + 41, 41, 41, 42, 42, 42, 43, 43, 43, 44, 44, + 45, 45, 45, 46, 46, 47, 47, 47, 48, 48, 48, + 49, 49, 50, 50, 50, 51, 51, 52, 52, 53, 53, + 53, 54, 54, 55, 55, 56, 56, 56, 57, 57, 58, + 58, 59, 59, 60, 60, 60, 61, 61, 62, 62, 63, + 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, + 69, 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, + 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, + 81, 81, 82, 82, 83, 84, 84, 85, 85, 86, 86, + 87, 88, 88, 89, 89, 90, 91, 91, 92, 93, 93, + 94, 95, 95, 96, 96, 97, 98, 98, 99, 100, 100, + 101, 102, 102, 103, 104, 104, 105, 106, 107, 107, 108, + 109, 109, 110, 111, 111, 112, 113, 114, 114, 115, 116, + 117, 117, 118, 119, 120, 120, 121, 122, 123, 123, 124, + 125, 126, 126, 127, 128, 129, 130, 130, 131, 132, 133, + 134, 135, 135, 136, 137, 138, 139, 140, 140, 141, 142, + 143, 144, 145, 146, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, + 208, 209, 210, 211, 213, 214, 215, 216, 217, 218, 219, + 221, 222, 223, 224, 225, 227, 228, 229, 230, 231, 233, + 234, 235, 236, 237, 239, 240, 241, 242, 244, 245, 246, + 247, 249, 250, 251, 253, 254, 255, 256, 258, 259, 260, + 262, 263, 264, 266, 267, 268, 270, 271, 272, 274, 275, + 277, 278, 279, 281, 282, 284, 285, 286, 288, 289, 291, + 292, 293, 295, 296, 298, 299, 301, 302, 304, 305, 307, + 308, 310, 311, 313, 314, 316, 317, 319, 320, 322, 323, + 325, 326, 328, 329, 331, 332, 334, 336, 337, 339, 340, + 342, 344, 345, 347, 348, 350, 352, 353, 355, 357, 358, + 360, 362, 363, 365, 367, 368, 370, 372, 373, 375, 377, + 378, 380, 382, 384, 385, 387, 389, 391, 392, 394, 396, + 398, 400, 401, 403, 405, 407, 409, 410, 412, 414, 416, + 418, 420, 421, 423, 425, 427, 429, 431, 433, 435, 436, + 438, 440, 442, 444, 446, 448, 450, 452, 454, 456, 458, + 460, 462, 464, 466, 468, 470, 472, 474, 476, 478, 480, + 482, 484, 486, 488, 490, 492, 494, 496, 498, 500, 503, + 505, 507, 509, 511, 513, 515, 517, 520, 522, 524, 526, + 528, 531, 533, 535, 537, 539, 542, 544, 546, 548, 550, + 553, 555, 557, 560, 562, 564, 566, 569, 571, 573, 576, + 578, 580, 583, 585, 587, 590, 592, 594, 597, 599, 602, + 604, 606, 609, 611, 614, 616, 618, 621, 623, 626, 628, + 631, 633, 636, 638, 641, 643, 646, 648, 651, 653, 656, + 658, 661, 664, 666, 669, 671, 674, 677, 679, 682, 684, + 687, 690, 692, 695, 698, 700, 703, 706, 708, 711, 714, + 716, 719, 722, 725, 727, 730, 733, 736, 738, 741, 744, + 747, 750, 752, 755, 758, 761, 764, 766, 769, 772, 775, + 778, 781, 784, 787, 789, 792, 795, 798, 801, 804, 807, + 810, 813, 816, 819, 822, 825, 828, 831, 834, 837, 840, + 843, 846, 849, 852, 855, 858, 862, 865, 868, 871, 874, + 877, 880, 883, 887, 890, 893, 896, 899, 902, 906, 909, + 912, 915, 919, 922, 925, 928, 932, 935, 938, 941, 945, + 948, 951, 955, 958, 961, 965, 968, 971, 975, 978, 982, + 985, 988, 992, 995, 999, 1002, 1006, 1009, 1013, 1016, 1020, + 1023, +}; + +static const u16 xgamma10_36[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, + 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, + 15, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, + 17, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, + 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, + 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, + 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, + 30, 31, 31, 31, 31, 32, 32, 32, 33, 33, 33, + 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 37, + 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, + 41, 41, 41, 42, 42, 43, 43, 43, 44, 44, 44, + 45, 45, 46, 46, 46, 47, 47, 47, 48, 48, 49, + 49, 49, 50, 50, 51, 51, 52, 52, 52, 53, 53, + 54, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, + 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63, + 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, + 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, + 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 81, + 81, 82, 82, 83, 83, 84, 85, 85, 86, 86, 87, + 88, 88, 89, 90, 90, 91, 91, 92, 93, 93, 94, + 95, 95, 96, 97, 97, 98, 99, 99, 100, 101, 101, + 102, 103, 103, 104, 105, 105, 106, 107, 107, 108, 109, + 110, 110, 111, 112, 112, 113, 114, 115, 115, 116, 117, + 118, 118, 119, 120, 121, 121, 122, 123, 124, 125, 125, + 126, 127, 128, 129, 129, 130, 131, 132, 133, 133, 134, + 135, 136, 137, 138, 138, 139, 140, 141, 142, 143, 144, + 145, 145, 146, 147, 148, 149, 150, 151, 152, 153, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 211, 212, 214, 215, 216, 217, 218, 219, 221, 222, 223, + 224, 225, 227, 228, 229, 230, 231, 233, 234, 235, 236, + 238, 239, 240, 241, 243, 244, 245, 247, 248, 249, 250, + 252, 253, 254, 256, 257, 258, 260, 261, 262, 264, 265, + 266, 268, 269, 271, 272, 273, 275, 276, 277, 279, 280, + 282, 283, 285, 286, 287, 289, 290, 292, 293, 295, 296, + 298, 299, 301, 302, 304, 305, 307, 308, 310, 311, 313, + 314, 316, 317, 319, 320, 322, 324, 325, 327, 328, 330, + 331, 333, 335, 336, 338, 339, 341, 343, 344, 346, 348, + 349, 351, 353, 354, 356, 358, 359, 361, 363, 364, 366, + 368, 370, 371, 373, 375, 377, 378, 380, 382, 384, 385, + 387, 389, 391, 393, 394, 396, 398, 400, 402, 403, 405, + 407, 409, 411, 413, 415, 416, 418, 420, 422, 424, 426, + 428, 430, 432, 434, 436, 438, 439, 441, 443, 445, 447, + 449, 451, 453, 455, 457, 459, 461, 463, 465, 467, 470, + 472, 474, 476, 478, 480, 482, 484, 486, 488, 490, 492, + 495, 497, 499, 501, 503, 505, 508, 510, 512, 514, 516, + 518, 521, 523, 525, 527, 530, 532, 534, 536, 539, 541, + 543, 545, 548, 550, 552, 555, 557, 559, 562, 564, 566, + 569, 571, 573, 576, 578, 580, 583, 585, 588, 590, 592, + 595, 597, 600, 602, 605, 607, 610, 612, 615, 617, 620, + 622, 625, 627, 630, 632, 635, 637, 640, 642, 645, 648, + 650, 653, 655, 658, 661, 663, 666, 669, 671, 674, 677, + 679, 682, 685, 687, 690, 693, 695, 698, 701, 704, 706, + 709, 712, 715, 717, 720, 723, 726, 729, 732, 734, 737, + 740, 743, 746, 749, 751, 754, 757, 760, 763, 766, 769, + 772, 775, 778, 781, 784, 787, 790, 793, 796, 799, 802, + 805, 808, 811, 814, 817, 820, 823, 826, 829, 832, 835, + 838, 842, 845, 848, 851, 854, 857, 860, 864, 867, 870, + 873, 876, 880, 883, 886, 889, 893, 896, 899, 903, 906, + 909, 912, 916, 919, 922, 926, 929, 932, 936, 939, 943, + 946, 949, 953, 956, 960, 963, 967, 970, 973, 977, 980, + 984, 987, 991, 994, 998, 1002, 1005, 1009, 1012, 1016, 1019, + 1023, +}; + +static const u16 xgamma10_37[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, + 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 14, + 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, + 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 20, + 20, 20, 20, 20, 21, 21, 21, 21, 21, 22, 22, + 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, + 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, + 31, 31, 31, 31, 32, 32, 32, 33, 33, 33, 33, + 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, + 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, 41, + 41, 41, 42, 42, 42, 43, 43, 44, 44, 44, 45, + 45, 45, 46, 46, 47, 47, 47, 48, 48, 49, 49, + 49, 50, 50, 51, 51, 51, 52, 52, 53, 53, 54, + 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, 58, + 59, 59, 60, 60, 61, 61, 62, 62, 63, 63, 64, + 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, + 70, 70, 71, 71, 72, 72, 73, 73, 74, 75, 75, + 76, 76, 77, 77, 78, 78, 79, 80, 80, 81, 81, + 82, 82, 83, 84, 84, 85, 85, 86, 87, 87, 88, + 89, 89, 90, 90, 91, 92, 92, 93, 94, 94, 95, + 96, 96, 97, 98, 98, 99, 100, 100, 101, 102, 102, + 103, 104, 104, 105, 106, 106, 107, 108, 109, 109, 110, + 111, 112, 112, 113, 114, 114, 115, 116, 117, 118, 118, + 119, 120, 121, 121, 122, 123, 124, 125, 125, 126, 127, + 128, 129, 129, 130, 131, 132, 133, 134, 134, 135, 136, + 137, 138, 139, 139, 140, 141, 142, 143, 144, 145, 146, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 197, 198, 199, 200, 201, + 202, 203, 204, 206, 207, 208, 209, 210, 211, 213, 214, + 215, 216, 217, 218, 220, 221, 222, 223, 225, 226, 227, + 228, 229, 231, 232, 233, 234, 236, 237, 238, 240, 241, + 242, 243, 245, 246, 247, 249, 250, 251, 253, 254, 255, + 257, 258, 259, 261, 262, 263, 265, 266, 268, 269, 270, + 272, 273, 275, 276, 277, 279, 280, 282, 283, 285, 286, + 288, 289, 291, 292, 294, 295, 297, 298, 300, 301, 303, + 304, 306, 307, 309, 310, 312, 313, 315, 316, 318, 320, + 321, 323, 324, 326, 328, 329, 331, 332, 334, 336, 337, + 339, 341, 342, 344, 346, 347, 349, 351, 352, 354, 356, + 358, 359, 361, 363, 364, 366, 368, 370, 372, 373, 375, + 377, 379, 380, 382, 384, 386, 388, 389, 391, 393, 395, + 397, 399, 401, 402, 404, 406, 408, 410, 412, 414, 416, + 418, 420, 421, 423, 425, 427, 429, 431, 433, 435, 437, + 439, 441, 443, 445, 447, 449, 451, 453, 455, 457, 459, + 462, 464, 466, 468, 470, 472, 474, 476, 478, 480, 483, + 485, 487, 489, 491, 493, 496, 498, 500, 502, 504, 507, + 509, 511, 513, 515, 518, 520, 522, 524, 527, 529, 531, + 534, 536, 538, 541, 543, 545, 548, 550, 552, 555, 557, + 559, 562, 564, 567, 569, 571, 574, 576, 579, 581, 584, + 586, 589, 591, 593, 596, 598, 601, 603, 606, 609, 611, + 614, 616, 619, 621, 624, 626, 629, 632, 634, 637, 639, + 642, 645, 647, 650, 653, 655, 658, 661, 663, 666, 669, + 672, 674, 677, 680, 682, 685, 688, 691, 694, 696, 699, + 702, 705, 708, 710, 713, 716, 719, 722, 725, 728, 730, + 733, 736, 739, 742, 745, 748, 751, 754, 757, 760, 763, + 766, 769, 772, 775, 778, 781, 784, 787, 790, 793, 796, + 799, 802, 805, 809, 812, 815, 818, 821, 824, 827, 831, + 834, 837, 840, 843, 847, 850, 853, 856, 860, 863, 866, + 869, 873, 876, 879, 883, 886, 889, 893, 896, 899, 903, + 906, 910, 913, 916, 920, 923, 927, 930, 934, 937, 940, + 944, 947, 951, 954, 958, 961, 965, 969, 972, 976, 979, + 983, 986, 990, 994, 997, 1001, 1005, 1008, 1012, 1016, 1019, + 1023, +}; + +static const u16 xgamma10_38[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, + 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, + 12, 12, 13, 13, 13, 13, 13, 13, 13, 14, 14, + 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 16, + 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, + 18, 18, 18, 18, 19, 19, 19, 19, 19, 20, 20, + 20, 20, 20, 21, 21, 21, 21, 21, 22, 22, 22, + 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 25, + 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 28, + 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 31, + 31, 31, 31, 32, 32, 32, 33, 33, 33, 33, 34, + 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, + 38, 38, 38, 39, 39, 39, 40, 40, 40, 41, 41, + 41, 42, 42, 43, 43, 43, 44, 44, 44, 45, 45, + 46, 46, 46, 47, 47, 47, 48, 48, 49, 49, 49, + 50, 50, 51, 51, 52, 52, 52, 53, 53, 54, 54, + 55, 55, 55, 56, 56, 57, 57, 58, 58, 59, 59, + 60, 60, 60, 61, 61, 62, 62, 63, 63, 64, 64, + 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, + 70, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, + 76, 77, 78, 78, 79, 79, 80, 81, 81, 82, 82, + 83, 83, 84, 85, 85, 86, 86, 87, 88, 88, 89, + 90, 90, 91, 92, 92, 93, 93, 94, 95, 95, 96, + 97, 97, 98, 99, 99, 100, 101, 102, 102, 103, 104, + 104, 105, 106, 106, 107, 108, 109, 109, 110, 111, 112, + 112, 113, 114, 115, 115, 116, 117, 118, 118, 119, 120, + 121, 122, 122, 123, 124, 125, 126, 126, 127, 128, 129, + 130, 130, 131, 132, 133, 134, 135, 136, 136, 137, 138, + 139, 140, 141, 142, 143, 143, 144, 145, 146, 147, 148, + 149, 150, 151, 152, 153, 153, 154, 155, 156, 157, 158, + 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 195, 196, 197, 198, 199, 200, 201, 203, 204, 205, + 206, 207, 208, 210, 211, 212, 213, 214, 216, 217, 218, + 219, 220, 222, 223, 224, 225, 227, 228, 229, 230, 232, + 233, 234, 235, 237, 238, 239, 241, 242, 243, 245, 246, + 247, 249, 250, 251, 253, 254, 255, 257, 258, 259, 261, + 262, 264, 265, 266, 268, 269, 271, 272, 274, 275, 276, + 278, 279, 281, 282, 284, 285, 287, 288, 290, 291, 293, + 294, 296, 297, 299, 300, 302, 303, 305, 307, 308, 310, + 311, 313, 314, 316, 318, 319, 321, 323, 324, 326, 327, + 329, 331, 332, 334, 336, 337, 339, 341, 342, 344, 346, + 348, 349, 351, 353, 354, 356, 358, 360, 361, 363, 365, + 367, 369, 370, 372, 374, 376, 378, 379, 381, 383, 385, + 387, 389, 391, 392, 394, 396, 398, 400, 402, 404, 406, + 408, 410, 412, 413, 415, 417, 419, 421, 423, 425, 427, + 429, 431, 433, 435, 437, 439, 441, 443, 446, 448, 450, + 452, 454, 456, 458, 460, 462, 464, 466, 469, 471, 473, + 475, 477, 479, 482, 484, 486, 488, 490, 493, 495, 497, + 499, 501, 504, 506, 508, 511, 513, 515, 517, 520, 522, + 524, 527, 529, 531, 534, 536, 538, 541, 543, 546, 548, + 550, 553, 555, 558, 560, 562, 565, 567, 570, 572, 575, + 577, 580, 582, 585, 587, 590, 592, 595, 597, 600, 603, + 605, 608, 610, 613, 616, 618, 621, 623, 626, 629, 631, + 634, 637, 639, 642, 645, 648, 650, 653, 656, 658, 661, + 664, 667, 669, 672, 675, 678, 681, 684, 686, 689, 692, + 695, 698, 701, 703, 706, 709, 712, 715, 718, 721, 724, + 727, 730, 733, 736, 739, 742, 745, 748, 751, 754, 757, + 760, 763, 766, 769, 772, 775, 778, 781, 785, 788, 791, + 794, 797, 800, 803, 807, 810, 813, 816, 820, 823, 826, + 829, 832, 836, 839, 842, 846, 849, 852, 856, 859, 862, + 866, 869, 872, 876, 879, 883, 886, 889, 893, 896, 900, + 903, 907, 910, 914, 917, 921, 924, 928, 931, 935, 938, + 942, 945, 949, 953, 956, 960, 964, 967, 971, 974, 978, + 982, 986, 989, 993, 997, 1000, 1004, 1008, 1012, 1015, 1019, + 1023, +}; + +static const u16 xgamma10_39[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, + 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, + 12, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, + 14, 14, 14, 15, 15, 15, 15, 15, 15, 16, 16, + 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, 18, + 18, 18, 18, 19, 19, 19, 19, 19, 20, 20, 20, + 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, + 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, + 28, 28, 29, 29, 29, 29, 30, 30, 30, 31, 31, + 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, + 35, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, + 38, 38, 39, 39, 39, 40, 40, 41, 41, 41, 42, + 42, 42, 43, 43, 43, 44, 44, 45, 45, 45, 46, + 46, 46, 47, 47, 48, 48, 48, 49, 49, 50, 50, + 51, 51, 51, 52, 52, 53, 53, 53, 54, 54, 55, + 55, 56, 56, 57, 57, 57, 58, 58, 59, 59, 60, + 60, 61, 61, 62, 62, 63, 63, 64, 64, 65, 65, + 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, + 71, 72, 73, 73, 74, 74, 75, 75, 76, 76, 77, + 78, 78, 79, 79, 80, 80, 81, 82, 82, 83, 83, + 84, 85, 85, 86, 87, 87, 88, 88, 89, 90, 90, + 91, 92, 92, 93, 94, 94, 95, 96, 96, 97, 98, + 98, 99, 100, 100, 101, 102, 102, 103, 104, 105, 105, + 106, 107, 107, 108, 109, 110, 110, 111, 112, 113, 113, + 114, 115, 116, 116, 117, 118, 119, 120, 120, 121, 122, + 123, 124, 124, 125, 126, 127, 128, 129, 129, 130, 131, + 132, 133, 134, 134, 135, 136, 137, 138, 139, 140, 141, + 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 190, 191, 192, 193, 194, 195, 196, + 198, 199, 200, 201, 202, 203, 204, 206, 207, 208, 209, + 210, 212, 213, 214, 215, 217, 218, 219, 220, 221, 223, + 224, 225, 227, 228, 229, 230, 232, 233, 234, 236, 237, + 238, 239, 241, 242, 243, 245, 246, 248, 249, 250, 252, + 253, 254, 256, 257, 259, 260, 261, 263, 264, 266, 267, + 269, 270, 271, 273, 274, 276, 277, 279, 280, 282, 283, + 285, 286, 288, 289, 291, 292, 294, 295, 297, 299, 300, + 302, 303, 305, 306, 308, 310, 311, 313, 314, 316, 318, + 319, 321, 323, 324, 326, 328, 329, 331, 333, 334, 336, + 338, 340, 341, 343, 345, 346, 348, 350, 352, 353, 355, + 357, 359, 361, 362, 364, 366, 368, 370, 372, 373, 375, + 377, 379, 381, 383, 385, 386, 388, 390, 392, 394, 396, + 398, 400, 402, 404, 406, 408, 410, 412, 414, 416, 418, + 420, 422, 424, 426, 428, 430, 432, 434, 436, 438, 440, + 442, 444, 446, 448, 451, 453, 455, 457, 459, 461, 463, + 466, 468, 470, 472, 474, 477, 479, 481, 483, 485, 488, + 490, 492, 494, 497, 499, 501, 504, 506, 508, 511, 513, + 515, 518, 520, 522, 525, 527, 529, 532, 534, 537, 539, + 541, 544, 546, 549, 551, 554, 556, 559, 561, 564, 566, + 569, 571, 574, 576, 579, 581, 584, 586, 589, 592, 594, + 597, 599, 602, 605, 607, 610, 613, 615, 618, 621, 623, + 626, 629, 632, 634, 637, 640, 643, 645, 648, 651, 654, + 656, 659, 662, 665, 668, 671, 673, 676, 679, 682, 685, + 688, 691, 694, 697, 700, 702, 705, 708, 711, 714, 717, + 720, 723, 726, 729, 732, 735, 739, 742, 745, 748, 751, + 754, 757, 760, 763, 766, 770, 773, 776, 779, 782, 786, + 789, 792, 795, 798, 802, 805, 808, 811, 815, 818, 821, + 825, 828, 831, 835, 838, 841, 845, 848, 852, 855, 858, + 862, 865, 869, 872, 876, 879, 883, 886, 890, 893, 897, + 900, 904, 907, 911, 914, 918, 922, 925, 929, 933, 936, + 940, 944, 947, 951, 955, 958, 962, 966, 969, 973, 977, + 981, 985, 988, 992, 996, 1000, 1004, 1007, 1011, 1015, 1019, + 1023, +}; + +static const u16 xgamma10_40[GAMMA10_TABLE_LENGTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, + 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, + 14, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, + 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, + 18, 18, 19, 19, 19, 19, 19, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, + 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, + 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, + 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, + 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, + 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, + 39, 39, 39, 40, 40, 40, 41, 41, 41, 42, 42, + 43, 43, 43, 44, 44, 44, 45, 45, 46, 46, 46, + 47, 47, 48, 48, 48, 49, 49, 50, 50, 50, 51, + 51, 52, 52, 53, 53, 53, 54, 54, 55, 55, 56, + 56, 57, 57, 57, 58, 58, 59, 59, 60, 60, 61, + 61, 62, 62, 63, 63, 64, 64, 65, 65, 66, 66, + 67, 67, 68, 68, 69, 69, 70, 70, 71, 72, 72, + 73, 73, 74, 74, 75, 75, 76, 77, 77, 78, 78, + 79, 79, 80, 81, 81, 82, 82, 83, 84, 84, 85, + 85, 86, 87, 87, 88, 89, 89, 90, 91, 91, 92, + 93, 93, 94, 95, 95, 96, 97, 97, 98, 99, 99, + 100, 101, 101, 102, 103, 104, 104, 105, 106, 106, 107, + 108, 109, 109, 110, 111, 112, 112, 113, 114, 115, 116, + 116, 117, 118, 119, 119, 120, 121, 122, 123, 123, 124, + 125, 126, 127, 128, 128, 129, 130, 131, 132, 133, 134, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 192, 193, 194, 195, 196, 197, 199, 200, 201, + 202, 203, 204, 206, 207, 208, 209, 210, 212, 213, 214, + 215, 217, 218, 219, 220, 222, 223, 224, 226, 227, 228, + 229, 231, 232, 233, 235, 236, 237, 239, 240, 241, 243, + 244, 245, 247, 248, 250, 251, 252, 254, 255, 257, 258, + 259, 261, 262, 264, 265, 267, 268, 270, 271, 273, 274, + 276, 277, 279, 280, 282, 283, 285, 286, 288, 289, 291, + 292, 294, 296, 297, 299, 300, 302, 304, 305, 307, 308, + 310, 312, 313, 315, 317, 318, 320, 322, 323, 325, 327, + 328, 330, 332, 333, 335, 337, 339, 340, 342, 344, 346, + 348, 349, 351, 353, 355, 357, 358, 360, 362, 364, 366, + 368, 369, 371, 373, 375, 377, 379, 381, 383, 385, 386, + 388, 390, 392, 394, 396, 398, 400, 402, 404, 406, 408, + 410, 412, 414, 416, 418, 420, 422, 424, 426, 429, 431, + 433, 435, 437, 439, 441, 443, 445, 448, 450, 452, 454, + 456, 458, 461, 463, 465, 467, 469, 472, 474, 476, 478, + 481, 483, 485, 488, 490, 492, 495, 497, 499, 501, 504, + 506, 509, 511, 513, 516, 518, 521, 523, 525, 528, 530, + 533, 535, 538, 540, 543, 545, 548, 550, 553, 555, 558, + 560, 563, 565, 568, 570, 573, 576, 578, 581, 583, 586, + 589, 591, 594, 597, 599, 602, 605, 607, 610, 613, 616, + 618, 621, 624, 627, 629, 632, 635, 638, 641, 643, 646, + 649, 652, 655, 658, 660, 663, 666, 669, 672, 675, 678, + 681, 684, 687, 690, 693, 696, 699, 702, 705, 708, 711, + 714, 717, 720, 723, 726, 729, 732, 735, 739, 742, 745, + 748, 751, 754, 758, 761, 764, 767, 770, 774, 777, 780, + 783, 787, 790, 793, 797, 800, 803, 807, 810, 813, 817, + 820, 824, 827, 830, 834, 837, 841, 844, 848, 851, 855, + 858, 862, 865, 869, 872, 876, 879, 883, 886, 890, 894, + 897, 901, 905, 908, 912, 916, 919, 923, 927, 930, 934, + 938, 942, 945, 949, 953, 957, 960, 964, 968, 972, 976, + 980, 984, 987, 991, 995, 999, 1003, 1007, 1011, 1015, 1019, + 1023, +}; + +static const u16 *xgamma10_curves[GAMMA_CURVE_LENGTH] = { + &xgamma10_01[0], + &xgamma10_02[0], + &xgamma10_03[0], + &xgamma10_04[0], + &xgamma10_05[0], + &xgamma10_06[0], + &xgamma10_07[0], + &xgamma10_08[0], + &xgamma10_09[0], + &xgamma10_10[0], + &xgamma10_11[0], + &xgamma10_12[0], + &xgamma10_13[0], + &xgamma10_14[0], + &xgamma10_15[0], + &xgamma10_16[0], + &xgamma10_17[0], + &xgamma10_18[0], + &xgamma10_19[0], + &xgamma10_20[0], + &xgamma10_21[0], + &xgamma10_22[0], + &xgamma10_23[0], + &xgamma10_24[0], + &xgamma10_25[0], + &xgamma10_26[0], + &xgamma10_27[0], + &xgamma10_28[0], + &xgamma10_29[0], + &xgamma10_30[0], + &xgamma10_31[0], + &xgamma10_32[0], + &xgamma10_33[0], + &xgamma10_34[0], + &xgamma10_35[0], + &xgamma10_36[0], + &xgamma10_37[0], + &xgamma10_38[0], + &xgamma10_39[0], + &xgamma10_40[0], +}; +#endif /* __XILINX_GAMMA_COEFF_H__ */ diff --git a/drivers/media/platform/xilinx/xilinx-gamma.c b/drivers/media/platform/xilinx/xilinx-gamma.c new file mode 100644 index 000000000000..d7996d0f34fa --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-gamma.c @@ -0,0 +1,543 @@ +/* + * Xilinx Gamma Correction IP + * + * Copyright (C) 2017 Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/xilinx-v4l2-controls.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-gamma-coeff.h" +#include "xilinx-vip.h" + +#define XGAMMA_MIN_HEIGHT (64) +#define XGAMMA_MAX_HEIGHT (4320) +#define XGAMMA_DEF_HEIGHT (720) +#define XGAMMA_MIN_WIDTH (64) +#define XGAMMA_MAX_WIDTH (8192) +#define XGAMMA_DEF_WIDTH (1280) + +#define XGAMMA_AP_CTRL (0x0000) +#define XGAMMA_GIE (0x0004) +#define XGAMMA_IER (0x0008) +#define XGAMMA_ISR (0x000c) +#define XGAMMA_WIDTH (0x0010) +#define XGAMMA_HEIGHT (0x0018) +#define XGAMMA_VIDEO_FORMAT (0x0020) +#define XGAMMA_GAMMA_LUT_0_BASE (0x0800) +#define XGAMMA_GAMMA_LUT_1_BASE (0x1000) +#define XGAMMA_GAMMA_LUT_2_BASE (0x1800) + +#define XGAMMA_RESET_DEASSERT (0) +#define XGAMMA_RESET_ASSERT (1) +#define XGAMMA_START BIT(0) +#define XGAMMA_AUTO_RESTART BIT(7) +#define XGAMMA_STREAM_ON (XGAMMA_START | XGAMMA_AUTO_RESTART) + +enum xgamma_video_format { + XGAMMA_RGB = 0, +}; + +/** + * struct xgamma_dev - Xilinx Video Gamma LUT device structure + * @xvip: Xilinx Video IP device + * @pads: Scaler sub-device media pads + * @formats: V4L2 media bus formats at the sink and source pads + * @default_formats: default V4L2 media bus formats + * @ctrl_handler: V4L2 Control Handler for R,G,B Gamma Controls + * @red_lut: Pointer to the gamma coefficient as per the Red Gamma control + * @green_lut: Pointer to the gamma coefficient as per the Green Gamma control + * @blue_lut: Pointer to the gamma coefficient as per the Blue Gamma control + * @color_depth: Color depth of the Video Gamma IP + * @gamma_table: Pointer to the table containing various gamma values + * @rst_gpio: GPIO reset line to bring VPSS Scaler out of reset + * @max_width: Maximum width supported by this instance. + * @max_height: Maximum height supported by this instance. + */ +struct xgamma_dev { + struct xvip_device xvip; + struct media_pad pads[2]; + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + struct v4l2_ctrl_handler ctrl_handler; + + const u16 *red_lut; + const u16 *green_lut; + const u16 *blue_lut; + u32 color_depth; + const u16 **gamma_table; + struct gpio_desc *rst_gpio; + u32 max_width; + u32 max_height; +}; + +static inline u32 xg_read(struct xgamma_dev *xg, u32 reg) +{ + u32 data; + + data = xvip_read(&xg->xvip, reg); + dev_dbg(xg->xvip.dev, + "Reading 0x%x from reg offset 0x%x", data, reg); + return data; +} + +static inline void xg_write(struct xgamma_dev *xg, u32 reg, u32 data) +{ + dev_dbg(xg->xvip.dev, + "Writing 0x%x to reg offset 0x%x", data, reg); + xvip_write(&xg->xvip, reg, data); +#ifdef DEBUG + if (xg_read(xg, reg) != data) + dev_err(xg->xvip.dev, + "Write 0x%x does not match read back", data); +#endif +} + +static inline struct xgamma_dev *to_xg(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xgamma_dev, xvip.subdev); +} + +static struct v4l2_mbus_framefmt * +__xg_get_pad_format(struct xgamma_dev *xg, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format( + &xg->xvip.subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xg->formats[pad]; + default: + return NULL; + } +} + +static void xg_set_lut_entries(struct xgamma_dev *xg, + const u16 *lut, const u32 lut_base) +{ + int itr; + u32 lut_offset, lut_data; + + lut_offset = lut_base; + /* Write LUT Entries */ + for (itr = 0; itr < BIT(xg->color_depth - 1); itr++) { + lut_data = (lut[2 * itr + 1] << 16) | lut[2 * itr]; + xg_write(xg, lut_offset, lut_data); + lut_offset += 4; + } +} + +static int xg_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xgamma_dev *xg = to_xg(subdev); + + if (!enable) { + dev_dbg(xg->xvip.dev, "%s : Off", __func__); + gpiod_set_value_cansleep(xg->rst_gpio, XGAMMA_RESET_ASSERT); + gpiod_set_value_cansleep(xg->rst_gpio, XGAMMA_RESET_DEASSERT); + return 0; + } + dev_dbg(xg->xvip.dev, "%s : Started", __func__); + + dev_dbg(xg->xvip.dev, "%s : Setting width %d and height %d", + __func__, xg->formats[XVIP_PAD_SINK].width, + xg->formats[XVIP_PAD_SINK].height); + xg_write(xg, XGAMMA_WIDTH, xg->formats[XVIP_PAD_SINK].width); + xg_write(xg, XGAMMA_HEIGHT, xg->formats[XVIP_PAD_SINK].height); + xg_write(xg, XGAMMA_VIDEO_FORMAT, XGAMMA_RGB); + xg_set_lut_entries(xg, xg->red_lut, XGAMMA_GAMMA_LUT_0_BASE); + xg_set_lut_entries(xg, xg->green_lut, XGAMMA_GAMMA_LUT_1_BASE); + xg_set_lut_entries(xg, xg->blue_lut, XGAMMA_GAMMA_LUT_2_BASE); + + /* Start GAMMA Correction LUT Video IP */ + xg_write(xg, XGAMMA_AP_CTRL, XGAMMA_STREAM_ON); + return 0; +} + +static const struct v4l2_subdev_video_ops xg_video_ops = { + .s_stream = xg_s_stream, +}; + +static int xg_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xgamma_dev *xg = to_xg(subdev); + + fmt->format = *__xg_get_pad_format(xg, cfg, fmt->pad, fmt->which); + return 0; +} + +static int xg_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xgamma_dev *xg = to_xg(subdev); + struct v4l2_mbus_framefmt *__format; + + __format = __xg_get_pad_format(xg, cfg, fmt->pad, fmt->which); + *__format = fmt->format; + + if (fmt->pad == XVIP_PAD_SINK) { + if (__format->code != MEDIA_BUS_FMT_RBG888_1X24) { + dev_dbg(xg->xvip.dev, + "Unsupported sink media bus code format"); + __format->code = MEDIA_BUS_FMT_RBG888_1X24; + } + } + __format->width = clamp_t(unsigned int, fmt->format.width, + XGAMMA_MIN_WIDTH, xg->max_width); + __format->height = clamp_t(unsigned int, fmt->format.height, + XGAMMA_MIN_HEIGHT, xg->max_height); + + fmt->format = *__format; + /* Propagate to Source Pad */ + __format = __xg_get_pad_format(xg, cfg, XVIP_PAD_SOURCE, fmt->which); + *__format = fmt->format; + return 0; +} + +static int xg_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct xgamma_dev *xg = to_xg(subdev); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK); + *format = xg->default_formats[XVIP_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE); + *format = xg->default_formats[XVIP_PAD_SOURCE]; + return 0; +} + +static int xg_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static const struct v4l2_subdev_internal_ops xg_internal_ops = { + .open = xg_open, + .close = xg_close, +}; + +static const struct v4l2_subdev_pad_ops xg_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xvip_enum_frame_size, + .get_fmt = xg_get_format, + .set_fmt = xg_set_format, +}; + +static const struct v4l2_subdev_ops xg_ops = { + .video = &xg_video_ops, + .pad = &xg_pad_ops, +}; + +static int +select_gamma(s32 value, const u16 **coeff, const u16 **xgamma_curves) +{ + if (!coeff) + return -EINVAL; + if (value <= 0 || value > GAMMA_CURVE_LENGTH) + return -EINVAL; + + *coeff = *(xgamma_curves + value - 1); + return 0; +} + +static int xg_s_ctrl(struct v4l2_ctrl *ctrl) +{ + int rval; + struct xgamma_dev *xg = + container_of(ctrl->handler, + struct xgamma_dev, ctrl_handler); + dev_dbg(xg->xvip.dev, "%s called", __func__); + switch (ctrl->id) { + case V4L2_CID_XILINX_GAMMA_CORR_RED_GAMMA: + rval = select_gamma(ctrl->val, &xg->red_lut, xg->gamma_table); + if (rval < 0) { + dev_err(xg->xvip.dev, "Invalid Red Gamma"); + return rval; + } + dev_dbg(xg->xvip.dev, "%s: Setting Red Gamma to %d.%d", + __func__, ctrl->val / 10, ctrl->val % 10); + xg_set_lut_entries(xg, xg->red_lut, XGAMMA_GAMMA_LUT_0_BASE); + break; + case V4L2_CID_XILINX_GAMMA_CORR_BLUE_GAMMA: + rval = select_gamma(ctrl->val, &xg->blue_lut, xg->gamma_table); + if (rval < 0) { + dev_err(xg->xvip.dev, "Invalid Blue Gamma"); + return rval; + } + dev_dbg(xg->xvip.dev, "%s: Setting Blue Gamma to %d.%d", + __func__, ctrl->val / 10, ctrl->val % 10); + xg_set_lut_entries(xg, xg->blue_lut, XGAMMA_GAMMA_LUT_1_BASE); + break; + case V4L2_CID_XILINX_GAMMA_CORR_GREEN_GAMMA: + rval = select_gamma(ctrl->val, &xg->green_lut, xg->gamma_table); + if (rval < 0) { + dev_err(xg->xvip.dev, "Invalid Green Gamma"); + return -EINVAL; + } + dev_dbg(xg->xvip.dev, "%s: Setting Green Gamma to %d.%d", + __func__, ctrl->val / 10, ctrl->val % 10); + xg_set_lut_entries(xg, xg->green_lut, XGAMMA_GAMMA_LUT_2_BASE); + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops xg_ctrl_ops = { + .s_ctrl = xg_s_ctrl, +}; + +static struct v4l2_ctrl_config xg_ctrls[] = { + /* Red Gamma */ + { + .ops = &xg_ctrl_ops, + .id = V4L2_CID_XILINX_GAMMA_CORR_RED_GAMMA, + .name = "Red Gamma Correction|1->0.1|10->1.0", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 40, + .step = 1, + .def = 10, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, + /* Blue Gamma */ + { + .ops = &xg_ctrl_ops, + .id = V4L2_CID_XILINX_GAMMA_CORR_BLUE_GAMMA, + .name = "Blue Gamma Correction|1->0.1|10->1.0", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 40, + .step = 1, + .def = 10, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, + /* Green Gamma */ + { + .ops = &xg_ctrl_ops, + .id = V4L2_CID_XILINX_GAMMA_CORR_GREEN_GAMMA, + .name = "Green Gamma Correction|1->0.1|10->1.0)", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 40, + .step = 1, + .def = 10, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, +}; + +static const struct media_entity_operations xg_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int xg_parse_of(struct xgamma_dev *xg) +{ + struct device *dev = xg->xvip.dev; + struct device_node *node = dev->of_node; + struct device_node *ports; + struct device_node *port; + u32 port_id = 0; + int rval; + + rval = of_property_read_u32(node, "xlnx,max-height", &xg->max_height); + if (rval < 0) { + dev_err(dev, "xlnx,max-height is missing!"); + return -EINVAL; + } else if (xg->max_height > XGAMMA_MAX_HEIGHT || + xg->max_height < XGAMMA_MIN_HEIGHT) { + dev_err(dev, "Invalid height in dt"); + return -EINVAL; + } + + rval = of_property_read_u32(node, "xlnx,max-width", &xg->max_width); + if (rval < 0) { + dev_err(dev, "xlnx,max-width is missing!"); + return -EINVAL; + } else if (xg->max_width > XGAMMA_MAX_WIDTH || + xg->max_width < XGAMMA_MIN_WIDTH) { + dev_err(dev, "Invalid width in dt"); + return -EINVAL; + } + + ports = of_get_child_by_name(node, "ports"); + if (!ports) + ports = node; + + /* Get the format description for each pad */ + for_each_child_of_node(ports, port) { + if (port->name && (of_node_cmp(port->name, "port") == 0)) { + rval = of_property_read_u32(port, "reg", &port_id); + if (rval < 0) { + dev_err(dev, "No reg in DT"); + return rval; + } + if (port_id != 0 && port_id != 1) { + dev_err(dev, "Invalid reg in DT"); + return -EINVAL; + } + + rval = of_property_read_u32(port, "xlnx,video-width", + &xg->color_depth); + if (rval < 0) { + dev_err(dev, "Missing xlnx-video-width in DT"); + return rval; + } + switch (xg->color_depth) { + case GAMMA_BPC_8: + xg->gamma_table = xgamma8_curves; + break; + case GAMMA_BPC_10: + xg->gamma_table = xgamma10_curves; + break; + default: + dev_err(dev, "Unsupported color depth %d", + xg->color_depth); + return -EINVAL; + } + } + } + + xg->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(xg->rst_gpio)) { + if (PTR_ERR(xg->rst_gpio) != -EPROBE_DEFER) + dev_err(dev, "Reset GPIO not setup in DT"); + return PTR_ERR(xg->rst_gpio); + } + return 0; +} + +static int xg_probe(struct platform_device *pdev) +{ + struct xgamma_dev *xg; + struct v4l2_subdev *subdev; + struct v4l2_mbus_framefmt *def_fmt; + int rval, itr; + + dev_dbg(&pdev->dev, "Gamma LUT Probe Started"); + xg = devm_kzalloc(&pdev->dev, sizeof(*xg), GFP_KERNEL); + if (!xg) + return -ENOMEM; + xg->xvip.dev = &pdev->dev; + rval = xg_parse_of(xg); + if (rval < 0) + return rval; + rval = xvip_init_resources(&xg->xvip); + + dev_dbg(xg->xvip.dev, "Reset Xilinx Video Gamma Corrrection"); + gpiod_set_value_cansleep(xg->rst_gpio, XGAMMA_RESET_DEASSERT); + + /* Init V4L2 subdev */ + subdev = &xg->xvip.subdev; + v4l2_subdev_init(subdev, &xg_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xg_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Default Formats Initialization */ + def_fmt = &xg->default_formats[XVIP_PAD_SINK]; + /* GAMMA LUT IP only to be supported for RGB */ + def_fmt->code = MEDIA_BUS_FMT_RBG888_1X24; + def_fmt->field = V4L2_FIELD_NONE; + def_fmt->colorspace = V4L2_COLORSPACE_SRGB; + def_fmt->width = XGAMMA_DEF_WIDTH; + def_fmt->height = XGAMMA_DEF_HEIGHT; + xg->formats[XVIP_PAD_SINK] = *def_fmt; + + def_fmt = &xg->default_formats[XVIP_PAD_SOURCE]; + *def_fmt = xg->default_formats[XVIP_PAD_SINK]; + xg->formats[XVIP_PAD_SOURCE] = *def_fmt; + + xg->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xg->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + /* Init Media Entity */ + subdev->entity.ops = &xg_media_ops; + rval = media_entity_pads_init(&subdev->entity, 2, xg->pads); + if (rval < 0) + goto media_error; + + /* V4L2 Controls */ + v4l2_ctrl_handler_init(&xg->ctrl_handler, ARRAY_SIZE(xg_ctrls)); + for (itr = 0; itr < ARRAY_SIZE(xg_ctrls); itr++) { + v4l2_ctrl_new_custom(&xg->ctrl_handler, + &xg_ctrls[itr], NULL); + } + if (xg->ctrl_handler.error) { + dev_err(&pdev->dev, "Failed to add V4L2 controls"); + rval = xg->ctrl_handler.error; + goto ctrl_error; + } + subdev->ctrl_handler = &xg->ctrl_handler; + rval = v4l2_ctrl_handler_setup(&xg->ctrl_handler); + if (rval < 0) { + dev_err(&pdev->dev, "Failed to setup control handler"); + goto ctrl_error; + } + + platform_set_drvdata(pdev, xg); + rval = v4l2_async_register_subdev(subdev); + if (rval < 0) { + dev_err(&pdev->dev, "failed to register subdev"); + goto v4l2_subdev_error; + } + dev_info(&pdev->dev, + "Xilinx %d-bit Video Gamma Correction LUT registered", + xg->color_depth); + return 0; +ctrl_error: + v4l2_ctrl_handler_free(&xg->ctrl_handler); +v4l2_subdev_error: + media_entity_cleanup(&subdev->entity); +media_error: + xvip_cleanup_resources(&xg->xvip); + return rval; +} + +static int xg_remove(struct platform_device *pdev) +{ + struct xgamma_dev *xg = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xg->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + /* Add entry to cleanup v4l2 control handle */ + media_entity_cleanup(&subdev->entity); + xvip_cleanup_resources(&xg->xvip); + return 0; +} + +static const struct of_device_id xg_of_id_table[] = { + {.compatible = "xlnx,v-gamma-lut"}, + { } +}; +MODULE_DEVICE_TABLE(of, xg_of_id_table); + +static struct platform_driver xg_driver = { + .driver = { + .name = "xilinx-gamma-lut", + .of_match_table = xg_of_id_table, + }, + .probe = xg_probe, + .remove = xg_remove, +}; + +module_platform_driver(xg_driver); +MODULE_DESCRIPTION("Xilinx Video Gamma Correction LUT Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-hls-common.h b/drivers/media/platform/xilinx/xilinx-hls-common.h new file mode 100644 index 000000000000..8ecc3cfb8a83 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-hls-common.h @@ -0,0 +1,36 @@ +/* + * Xilinx HLS common header + * + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Radhey Shyam Pandey <radheys@xilinx.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __XILINX_HLS_COMMON_H__ +#define __XILINX_HLS_COMMON_H__ + +#include <linux/bitops.h> + +#define XHLS_DEF_WIDTH 1920 +#define XHLS_DEF_HEIGHT 1080 + +#define XHLS_REG_CTRL_DONE BIT(1) +#define XHLS_REG_CTRL_IDLE BIT(2) +#define XHLS_REG_CTRL_READY BIT(3) +#define XHLS_REG_CTRL_AUTO_RESTART BIT(7) +#define XHLS_REG_GIE 0x04 +#define XHLS_REG_GIE_GIE BIT(0) +#define XHLS_REG_IER 0x08 +#define XHLS_REG_IER_DONE BIT(0) +#define XHLS_REG_IER_READY BIT(1) +#define XHLS_REG_ISR 0x0c +#define XHLS_REG_ISR_DONE BIT(0) +#define XHLS_REG_ISR_READY BIT(1) +#define XHLS_REG_ROWS 0x10 +#define XHLS_REG_COLS 0x18 + +#endif /* __XILINX_HLS_COMMON_H__ */ diff --git a/drivers/media/platform/xilinx/xilinx-hls.c b/drivers/media/platform/xilinx/xilinx-hls.c new file mode 100644 index 000000000000..fc42977440a9 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-hls.c @@ -0,0 +1,481 @@ +/* + * Xilinx HLS Core + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/xilinx-hls.h> +#include <linux/xilinx-v4l2-controls.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-hls-common.h" +#include "xilinx-vip.h" + +/** + * struct xhls_device - Xilinx HLS Core device structure + * @xvip: Xilinx Video IP device + * @pads: media pads + * @compatible: first DT compatible string for the device + * @formats: active V4L2 media bus formats at the sink and source pads + * @default_formats: default V4L2 media bus formats + * @vip_formats: format information corresponding to the pads active formats + * @model: additional description of IP implementation if available + * @ctrl_handler: control handler + * @user_mem: user portion of the register space + * @user_mem_size: size of the user portion of the register space + */ +struct xhls_device { + struct xvip_device xvip; + struct media_pad pads[2]; + + const char *compatible; + + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct xvip_video_format *vip_formats[2]; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *model; + + void __iomem *user_mem; + size_t user_mem_size; +}; + +static inline struct xhls_device *to_hls(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xhls_device, xvip.subdev); +} + +/* ----------------------------------------------------------------------------- + * Controls + */ + +static const struct v4l2_ctrl_config xhls_model_ctrl = { + .id = V4L2_CID_XILINX_HLS_MODEL, + .name = "HLS Model", + .type = V4L2_CTRL_TYPE_STRING, + .step = 1, + .flags = V4L2_CTRL_FLAG_READ_ONLY, +}; + +static int xhls_create_controls(struct xhls_device *xhls) +{ + struct v4l2_ctrl_config model = xhls_model_ctrl; + struct v4l2_ctrl *ctrl; + + model.max = strlen(xhls->compatible); + model.min = model.max; + + v4l2_ctrl_handler_init(&xhls->ctrl_handler, 1); + + ctrl = v4l2_ctrl_new_custom(&xhls->ctrl_handler, &model, NULL); + + if (xhls->ctrl_handler.error) { + dev_err(xhls->xvip.dev, "failed to add controls\n"); + return xhls->ctrl_handler.error; + } + + v4l2_ctrl_s_ctrl_string(ctrl, xhls->compatible); + + xhls->xvip.subdev.ctrl_handler = &xhls->ctrl_handler; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static int xhls_user_read(struct xhls_device *xhls, + struct xilinx_axi_hls_registers *regs) +{ + unsigned int i; + u32 offset; + u32 value; + + if (regs->num_regs >= xhls->user_mem_size / 4) + return -EINVAL; + + for (i = 0; i < regs->num_regs; ++i) { + if (copy_from_user(&offset, ®s->regs[i].offset, + sizeof(offset))) + return -EFAULT; + + if (offset >= xhls->user_mem_size || offset & 3) + return -EINVAL; + + value = ioread32(xhls->user_mem + offset); + + if (copy_to_user(®s->regs[i].value, &value, sizeof(value))) + return -EFAULT; + } + + return 0; +} + +static int xhls_user_write(struct xhls_device *xhls, + struct xilinx_axi_hls_registers *regs) +{ + struct xilinx_axi_hls_register reg; + unsigned int i; + + if (regs->num_regs >= xhls->user_mem_size / 4) + return -EINVAL; + + for (i = 0; i < regs->num_regs; ++i) { + if (copy_from_user(®, ®s->regs[i], sizeof(reg))) + return -EFAULT; + + if (reg.offset >= xhls->user_mem_size || reg.offset & 3) + return -EINVAL; + + iowrite32(reg.value, xhls->user_mem + reg.offset); + } + + return 0; +} + +static long xhls_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg) +{ + struct xhls_device *xhls = to_hls(subdev); + + switch (cmd) { + case XILINX_AXI_HLS_READ: + return xhls_user_read(xhls, arg); + case XILINX_AXI_HLS_WRITE: + return xhls_user_write(xhls, arg); + } + + return -ENOTTY; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Video Operations + */ + +static int xhls_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xhls_device *xhls = to_hls(subdev); + struct v4l2_mbus_framefmt *format = &xhls->formats[XVIP_PAD_SINK]; + + if (!enable) { + xvip_write(&xhls->xvip, XVIP_CTRL_CONTROL, 0); + return 0; + } + + xvip_write(&xhls->xvip, XHLS_REG_COLS, format->width); + xvip_write(&xhls->xvip, XHLS_REG_ROWS, format->height); + + xvip_write(&xhls->xvip, XVIP_CTRL_CONTROL, + XHLS_REG_CTRL_AUTO_RESTART | XVIP_CTRL_CONTROL_SW_ENABLE); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static struct v4l2_mbus_framefmt * +__xhls_get_pad_format(struct xhls_device *xhls, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xhls->xvip.subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xhls->formats[pad]; + default: + return NULL; + } +} + +static int xhls_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xhls_device *xhls = to_hls(subdev); + + fmt->format = *__xhls_get_pad_format(xhls, cfg, fmt->pad, fmt->which); + + return 0; +} + +static int xhls_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xhls_device *xhls = to_hls(subdev); + struct v4l2_mbus_framefmt *format; + + format = __xhls_get_pad_format(xhls, cfg, fmt->pad, fmt->which); + + if (fmt->pad == XVIP_PAD_SOURCE) { + fmt->format = *format; + return 0; + } + + xvip_set_format_size(format, fmt); + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = __xhls_get_pad_format(xhls, cfg, XVIP_PAD_SOURCE, + fmt->which); + + xvip_set_format_size(format, fmt); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static int xhls_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct xhls_device *xhls = to_hls(subdev); + struct v4l2_mbus_framefmt *format; + + /* Initialize with default formats */ + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK); + *format = xhls->default_formats[XVIP_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE); + *format = xhls->default_formats[XVIP_PAD_SOURCE]; + + return 0; +} + +static int xhls_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static struct v4l2_subdev_core_ops xhls_core_ops = { + .ioctl = xhls_ioctl, +}; + +static struct v4l2_subdev_video_ops xhls_video_ops = { + .s_stream = xhls_s_stream, +}; + +static struct v4l2_subdev_pad_ops xhls_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xvip_enum_frame_size, + .get_fmt = xhls_get_format, + .set_fmt = xhls_set_format, +}; + +static struct v4l2_subdev_ops xhls_ops = { + .core = &xhls_core_ops, + .video = &xhls_video_ops, + .pad = &xhls_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xhls_internal_ops = { + .open = xhls_open, + .close = xhls_close, +}; + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static const struct media_entity_operations xhls_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static void xhls_init_formats(struct xhls_device *xhls) +{ + struct v4l2_mbus_framefmt *format; + + /* Initialize default and active formats */ + format = &xhls->default_formats[XVIP_PAD_SINK]; + format->code = xhls->vip_formats[XVIP_PAD_SINK]->code; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + format->width = xvip_read(&xhls->xvip, XHLS_REG_COLS); + format->height = xvip_read(&xhls->xvip, XHLS_REG_ROWS); + + xhls->formats[XVIP_PAD_SINK] = *format; + + format = &xhls->default_formats[XVIP_PAD_SOURCE]; + *format = xhls->default_formats[XVIP_PAD_SINK]; + format->code = xhls->vip_formats[XVIP_PAD_SOURCE]->code; + + xhls->formats[XVIP_PAD_SOURCE] = *format; +} + +static int xhls_parse_of(struct xhls_device *xhls) +{ + struct device *dev = xhls->xvip.dev; + struct device_node *node = xhls->xvip.dev->of_node; + struct device_node *ports; + struct device_node *port; + u32 port_id; + int ret; + + ret = of_property_read_string(node, "compatible", &xhls->compatible); + if (ret < 0) + return -EINVAL; + + ports = of_get_child_by_name(node, "ports"); + if (ports == NULL) + ports = node; + + /* Get the format description for each pad */ + for_each_child_of_node(ports, port) { + if (port->name && (of_node_cmp(port->name, "port") == 0)) { + const struct xvip_video_format *vip_format; + + vip_format = xvip_of_get_format(port); + if (IS_ERR(vip_format)) { + dev_err(dev, "invalid format in DT"); + return PTR_ERR(vip_format); + } + + ret = of_property_read_u32(port, "reg", &port_id); + if (ret < 0) { + dev_err(dev, "no reg in DT"); + return ret; + } + + if (port_id != 0 && port_id != 1) { + dev_err(dev, "invalid reg in DT"); + return -EINVAL; + } + + xhls->vip_formats[port_id] = vip_format; + } + } + + return 0; +} + +static int xhls_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct xhls_device *xhls; + struct resource *mem; + int ret; + + xhls = devm_kzalloc(&pdev->dev, sizeof(*xhls), GFP_KERNEL); + if (!xhls) + return -ENOMEM; + + xhls->xvip.dev = &pdev->dev; + + ret = xhls_parse_of(xhls); + if (ret < 0) + return ret; + + ret = xvip_init_resources(&xhls->xvip); + if (ret < 0) + return ret; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + xhls->user_mem = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(xhls->user_mem)) + return PTR_ERR(xhls->user_mem); + xhls->user_mem_size = resource_size(mem); + + /* Reset and initialize the core */ + xvip_reset(&xhls->xvip); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xhls->xvip.subdev; + v4l2_subdev_init(subdev, &xhls_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xhls_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xhls); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + xhls_init_formats(xhls); + + xhls->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xhls->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &xhls_media_ops; + ret = media_entity_pads_init(&subdev->entity, 2, xhls->pads); + if (ret < 0) + goto error; + + ret = xhls_create_controls(xhls); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, xhls); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + dev_info(xhls->xvip.dev, "device %s found\n", xhls->compatible); + + return 0; + +error: + v4l2_ctrl_handler_free(&xhls->ctrl_handler); + media_entity_cleanup(&subdev->entity); + xvip_cleanup_resources(&xhls->xvip); + return ret; +} + +static int xhls_remove(struct platform_device *pdev) +{ + struct xhls_device *xhls = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xhls->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&xhls->ctrl_handler); + media_entity_cleanup(&subdev->entity); + + xvip_cleanup_resources(&xhls->xvip); + + return 0; +} + +static const struct of_device_id xhls_of_id_table[] = { + { .compatible = "xlnx,v-hls" }, + { } +}; +MODULE_DEVICE_TABLE(of, xhls_of_id_table); + +static struct platform_driver xhls_driver = { + .driver = { + .name = "xilinx-hls", + .of_match_table = xhls_of_id_table, + }, + .probe = xhls_probe, + .remove = xhls_remove, +}; + +module_platform_driver(xhls_driver); + +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_DESCRIPTION("Xilinx HLS Core Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-m2m.c b/drivers/media/platform/xilinx/xilinx-m2m.c new file mode 100644 index 000000000000..5a932a45e571 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-m2m.c @@ -0,0 +1,2106 @@ +//SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx V4L2 mem2mem driver + * + * Copyright (C) 2017-2018 Xilinx, Inc. + * + * Author: Satish Kumar Nagireddy <satish.nagireddy.nagireddy@xilinx.com> + */ + +#include <drm/drm_fourcc.h> +#include <linux/delay.h> +#include <linux/dma/xilinx_frmbuf.h> +#include <linux/lcm.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> + +#include "xilinx-vip.h" + +#define XVIP_M2M_NAME "xilinx-mem2mem" +#define XVIP_M2M_DEFAULT_FMT V4L2_PIX_FMT_RGB24 + +/* Minimum and maximum widths are expressed in bytes */ +#define XVIP_M2M_MIN_WIDTH 1U +#define XVIP_M2M_MAX_WIDTH 65535U +#define XVIP_M2M_MIN_HEIGHT 1U +#define XVIP_M2M_MAX_HEIGHT 8191U + +#define XVIP_M2M_DEF_WIDTH 1920 +#define XVIP_M2M_DEF_HEIGHT 1080 + +#define XVIP_M2M_PAD_SINK 1 +#define XVIP_M2M_PAD_SOURCE 0 + +/** + * struct xvip_graph_entity - Entity in the video graph + * @list: list entry in a graph entities list + * @node: the entity's DT node + * @entity: media entity, from the corresponding V4L2 subdev + * @asd: subdev asynchronous registration information + * @subdev: V4L2 subdev + * @streaming: status of the V4L2 subdev if streaming or not + */ +struct xvip_graph_entity { + struct list_head list; + struct device_node *node; + struct media_entity *entity; + + struct v4l2_async_subdev asd; + struct v4l2_subdev *subdev; + bool streaming; +}; + +/** + * struct xvip_pipeline - Xilinx Video IP pipeline structure + * @pipe: media pipeline + * @lock: protects the pipeline @stream_count + * @use_count: number of DMA engines using the pipeline + * @stream_count: number of DMA engines currently streaming + * @num_dmas: number of DMA engines in the pipeline + * @xdev: Composite device the pipe belongs to + */ +struct xvip_pipeline { + struct media_pipeline pipe; + + /* protects the pipeline @stream_count */ + struct mutex lock; + unsigned int use_count; + unsigned int stream_count; + + unsigned int num_dmas; + struct xvip_m2m_dev *xdev; +}; + +struct xventity_list { + struct list_head list; + struct media_entity *entity; +}; + +/** + * struct xvip_m2m_dev - Xilinx Video mem2mem device structure + * @v4l2_dev: V4L2 device + * @dev: (OF) device + * @media_dev: media device + * @notifier: V4L2 asynchronous subdevs notifier + * @entities: entities in the graph as a list of xvip_graph_entity + * @num_subdevs: number of subdevs in the pipeline + * @lock: This is to protect mem2mem context structure data + * @queued_lock: This is to protect video buffer information + * @dma: Video DMA channels + * @m2m_dev: V4L2 mem2mem device structure + * @v4l2_caps: V4L2 capabilities of the whole device + */ +struct xvip_m2m_dev { + struct v4l2_device v4l2_dev; + struct device *dev; + + struct media_device media_dev; + struct v4l2_async_notifier notifier; + struct list_head entities; + unsigned int num_subdevs; + + /* Protects to m2m context data */ + struct mutex lock; + + /* Protects vb2_v4l2_buffer data */ + spinlock_t queued_lock; + struct xvip_m2m_dma *dma; + struct v4l2_m2m_dev *m2m_dev; + u32 v4l2_caps; +}; + +static inline struct xvip_pipeline *to_xvip_pipeline(struct media_entity *e) +{ + return container_of(e->pipe, struct xvip_pipeline, pipe); +} + +/** + * struct xvip_m2m_dma - Video DMA channel + * @video: V4L2 video device associated with the DMA channel + * @xdev: composite mem2mem device the DMA channels belongs to + * @chan_tx: DMA engine channel for MEM2DEV transfer + * @chan_rx: DMA engine channel for DEV2MEM transfer + * @outfmt: active V4L2 OUTPUT port pixel format + * @capfmt: active V4L2 CAPTURE port pixel format + * @r: crop rectangle parameters + * @outinfo: format information corresponding to the active @outfmt + * @capinfo: format information corresponding to the active @capfmt + * @align: transfer alignment required by the DMA channel (in bytes) + * @crop: boolean flag to indicate if crop is requested + * @pads: media pads for the video M2M device entity + * @pipe: pipeline belonging to the DMA channel + */ +struct xvip_m2m_dma { + struct video_device video; + struct xvip_m2m_dev *xdev; + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; + struct v4l2_format outfmt; + struct v4l2_format capfmt; + struct v4l2_rect r; + const struct xvip_video_format *outinfo; + const struct xvip_video_format *capinfo; + u32 align; + bool crop; + + struct media_pad pads[2]; + struct xvip_pipeline pipe; +}; + +/** + * struct xvip_m2m_ctx - VIPP mem2mem context + * @fh: V4L2 file handler + * @xdev: composite mem2mem device the DMA channels belongs to + * @xt: dma interleaved template for dma configuration + * @sgl: data chunk structure for dma_interleaved_template + */ +struct xvip_m2m_ctx { + struct v4l2_fh fh; + struct xvip_m2m_dev *xdev; + struct dma_interleaved_template xt; + struct data_chunk sgl[1]; +}; + +static inline struct xvip_m2m_ctx *file2ctx(struct file *file) +{ + return container_of(file->private_data, struct xvip_m2m_ctx, fh); +} + +static struct v4l2_subdev * +xvip_dma_remote_subdev(struct media_pad *local, u32 *pad) +{ + struct media_pad *remote; + + remote = media_entity_remote_pad(local); + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int xvip_dma_verify_format(struct xvip_m2m_dma *dma) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + int ret; + int width, height; + + subdev = xvip_dma_remote_subdev(&dma->pads[XVIP_PAD_SOURCE], &fmt.pad); + if (!subdev) + return -EPIPE; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + if (dma->outinfo->code != fmt.format.code) + return -EINVAL; + + if (V4L2_TYPE_IS_MULTIPLANAR(dma->outfmt.type)) { + width = dma->outfmt.fmt.pix_mp.width; + height = dma->outfmt.fmt.pix_mp.height; + } else { + width = dma->outfmt.fmt.pix.width; + height = dma->outfmt.fmt.pix.height; + } + + if (width != fmt.format.width || height != fmt.format.height) + return -EINVAL; + + return 0; +} + +#define to_xvip_dma(vdev) container_of(vdev, struct xvip_m2m_dma, video) +/* ----------------------------------------------------------------------------- + * Pipeline Stream Management + */ + +/** + * xvip_subdev_set_streaming - Find and update streaming status of subdev + * @xdev: Composite video device + * @subdev: V4L2 sub-device + * @enable: enable/disable streaming status + * + * Walk the xvip graph entities list and find if subdev is present. Returns + * streaming status of subdev and update the status as requested + * + * Return: streaming status (true or false) if successful or warn_on if subdev + * is not present and return false + */ +static bool xvip_subdev_set_streaming(struct xvip_m2m_dev *xdev, + struct v4l2_subdev *subdev, bool enable) +{ + struct xvip_graph_entity *entity; + + list_for_each_entry(entity, &xdev->entities, list) + if (entity->node == subdev->dev->of_node) { + bool status = entity->streaming; + + entity->streaming = enable; + return status; + } + + WARN(1, "Should never get here\n"); + return false; +} + +static int xvip_entity_start_stop(struct xvip_m2m_dev *xdev, + struct media_entity *entity, bool start) +{ + struct v4l2_subdev *subdev; + bool is_streaming; + int ret = 0; + + dev_dbg(xdev->dev, "%s entity %s\n", + start ? "Starting" : "Stopping", entity->name); + subdev = media_entity_to_v4l2_subdev(entity); + + /* This is to maintain list of stream on/off devices */ + is_streaming = xvip_subdev_set_streaming(xdev, subdev, start); + + /* + * start or stop the subdev only once in case if they are + * shared between sub-graphs + */ + if (start && !is_streaming) { + /* power-on subdevice */ + ret = v4l2_subdev_call(subdev, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_err(xdev->dev, + "s_power on failed on subdev\n"); + xvip_subdev_set_streaming(xdev, subdev, 0); + return ret; + } + + /* stream-on subdevice */ + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_err(xdev->dev, + "s_stream on failed on subdev\n"); + v4l2_subdev_call(subdev, core, s_power, 0); + xvip_subdev_set_streaming(xdev, subdev, 0); + } + } else if (!start && is_streaming) { + /* stream-off subdevice */ + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_err(xdev->dev, + "s_stream off failed on subdev\n"); + xvip_subdev_set_streaming(xdev, subdev, 1); + } + + /* power-off subdevice */ + ret = v4l2_subdev_call(subdev, core, s_power, 0); + if (ret < 0 && ret != -ENOIOCTLCMD) + dev_err(xdev->dev, + "s_power off failed on subdev\n"); + } + + return ret; +} + +/** + * xvip_pipeline_start_stop - Start ot stop streaming on a pipeline + * @xdev: Composite video device + * @dma: xvip dma + * @start: Start (when true) or stop (when false) the pipeline + * + * Walk the entities chain starting @dma and start or stop all of them + * + * Return: 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int xvip_pipeline_start_stop(struct xvip_m2m_dev *xdev, + struct xvip_m2m_dma *dma, bool start) +{ + struct media_graph graph; + struct media_entity *entity = &dma->video.entity; + struct media_device *mdev = entity->graph_obj.mdev; + struct xventity_list *temp, *_temp; + LIST_HEAD(ent_list); + int ret = 0; + + mutex_lock(&mdev->graph_mutex); + + /* Walk the graph to locate the subdev nodes */ + ret = media_graph_walk_init(&graph, mdev); + if (ret) + goto error; + + media_graph_walk_start(&graph, entity); + + /* get the list of entities */ + while ((entity = media_graph_walk_next(&graph))) { + struct xventity_list *ele; + + /* We want to stream on/off only subdevs */ + if (!is_media_entity_v4l2_subdev(entity)) + continue; + + /* Maintain the pipeline sequence in a list */ + ele = kzalloc(sizeof(*ele), GFP_KERNEL); + if (!ele) { + ret = -ENOMEM; + goto error; + } + + ele->entity = entity; + list_add(&ele->list, &ent_list); + } + + if (start) { + list_for_each_entry_safe(temp, _temp, &ent_list, list) { + /* Enable all subdevs from sink to source */ + ret = xvip_entity_start_stop(xdev, temp->entity, start); + if (ret < 0) { + dev_err(xdev->dev, "ret = %d for entity %s\n", + ret, temp->entity->name); + break; + } + } + } else { + list_for_each_entry_safe_reverse(temp, _temp, &ent_list, list) + /* Enable all subdevs from source to sink */ + xvip_entity_start_stop(xdev, temp->entity, start); + } + + list_for_each_entry_safe(temp, _temp, &ent_list, list) { + list_del(&temp->list); + kfree(temp); + } + +error: + mutex_unlock(&mdev->graph_mutex); + media_graph_walk_cleanup(&graph); + return ret; +} + +/** + * xvip_pipeline_set_stream - Enable/disable streaming on a pipeline + * @pipe: The pipeline + * @on: Turn the stream on when true or off when false + * + * The pipeline is shared between all DMA engines connect at its input and + * output. While the stream state of DMA engines can be controlled + * independently, pipelines have a shared stream state that enable or disable + * all entities in the pipeline. For this reason the pipeline uses a streaming + * counter that tracks the number of DMA engines that have requested the stream + * to be enabled. This will walk the graph starting from each DMA and enable or + * disable the entities in the path. + * + * When called with the @on argument set to true, this function will increment + * the pipeline streaming count. If the streaming count reaches the number of + * DMA engines in the pipeline it will enable all entities that belong to the + * pipeline. + * + * Similarly, when called with the @on argument set to false, this function will + * decrement the pipeline streaming count and disable all entities in the + * pipeline when the streaming count reaches zero. + * + * Return: 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. Stopping the pipeline never fails. The pipeline state is + * not updated when the operation fails. + */ +static int xvip_pipeline_set_stream(struct xvip_pipeline *pipe, bool on) +{ + struct xvip_m2m_dev *xdev; + struct xvip_m2m_dma *dma; + int ret = 0; + + mutex_lock(&pipe->lock); + xdev = pipe->xdev; + dma = xdev->dma; + + if (on) { + ret = xvip_pipeline_start_stop(xdev, dma, true); + if (ret < 0) + goto done; + pipe->stream_count++; + } else { + if (--pipe->stream_count == 0) + xvip_pipeline_start_stop(xdev, dma, false); + } + +done: + mutex_unlock(&pipe->lock); + return ret; +} + +static int xvip_pipeline_validate(struct xvip_pipeline *pipe, + struct xvip_m2m_dma *start) +{ + struct media_graph graph; + struct media_entity *entity = &start->video.entity; + struct media_device *mdev = entity->graph_obj.mdev; + unsigned int num_inputs = 0; + unsigned int num_outputs = 0; + int ret; + + mutex_lock(&mdev->graph_mutex); + + /* Walk the graph to locate the video nodes. */ + ret = media_graph_walk_init(&graph, mdev); + if (ret) { + mutex_unlock(&mdev->graph_mutex); + return ret; + } + + media_graph_walk_start(&graph, entity); + + while ((entity = media_graph_walk_next(&graph))) { + struct xvip_m2m_dma *dma; + + if (entity->function != MEDIA_ENT_F_IO_V4L) + continue; + + dma = to_xvip_dma(media_entity_to_video_device(entity)); + + num_outputs++; + num_inputs++; + } + + mutex_unlock(&mdev->graph_mutex); + + media_graph_walk_cleanup(&graph); + + /* We need at least one DMA to proceed */ + if (num_outputs == 0 && num_inputs == 0) + return -EPIPE; + + pipe->num_dmas = num_inputs + num_outputs; + pipe->xdev = start->xdev; + + return 0; +} + +static void __xvip_pipeline_cleanup(struct xvip_pipeline *pipe) +{ + pipe->num_dmas = 0; +} + +/** + * xvip_pipeline_cleanup - Cleanup the pipeline after streaming + * @pipe: the pipeline + * + * Decrease the pipeline use count and clean it up if we were the last user. + */ +static void xvip_pipeline_cleanup(struct xvip_pipeline *pipe) +{ + mutex_lock(&pipe->lock); + + /* If we're the last user clean up the pipeline. */ + if (--pipe->use_count == 0) + __xvip_pipeline_cleanup(pipe); + + mutex_unlock(&pipe->lock); +} + +/** + * xvip_pipeline_prepare - Prepare the pipeline for streaming + * @pipe: the pipeline + * @dma: DMA engine at one end of the pipeline + * + * Validate the pipeline if no user exists yet, otherwise just increase the use + * count. + * + * Return: 0 if successful or -EPIPE if the pipeline is not valid. + */ +static int xvip_pipeline_prepare(struct xvip_pipeline *pipe, + struct xvip_m2m_dma *dma) +{ + int ret; + + mutex_lock(&pipe->lock); + + /* If we're the first user validate and initialize the pipeline. */ + if (pipe->use_count == 0) { + ret = xvip_pipeline_validate(pipe, dma); + if (ret < 0) { + __xvip_pipeline_cleanup(pipe); + goto done; + } + } + + pipe->use_count++; + ret = 0; + +done: + mutex_unlock(&pipe->lock); + return ret; +} + +static void xvip_m2m_dma_callback_mem2dev(void *data) +{ +} + +static void xvip_m2m_dma_callback(void *data) +{ + struct xvip_m2m_ctx *ctx = data; + struct xvip_m2m_dev *xdev = ctx->xdev; + struct vb2_v4l2_buffer *src_vb, *dst_vb; + + spin_lock(&xdev->queued_lock); + src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; + dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_vb->flags |= + src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_vb->timecode = src_vb->timecode; + + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); + v4l2_m2m_job_finish(xdev->m2m_dev, ctx->fh.m2m_ctx); + spin_unlock(&xdev->queued_lock); +} + +/* + * Queue operations + */ + +static int xvip_m2m_queue_setup(struct vb2_queue *vq, + u32 *nbuffers, u32 *nplanes, + u32 sizes[], struct device *alloc_devs[]) +{ + struct xvip_m2m_ctx *ctx = vb2_get_drv_priv(vq); + struct xvip_m2m_dma *dma = ctx->xdev->dma; + struct v4l2_format *f; + const struct xvip_video_format *info; + u32 i; + + if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + f = &dma->outfmt; + info = dma->outinfo; + } else { + f = &dma->capfmt; + info = dma->capinfo; + } + + if (*nplanes) { + if (*nplanes != f->fmt.pix_mp.num_planes) + return -EINVAL; + + for (i = 0; i < *nplanes; i++) { + if (sizes[i] < f->fmt.pix_mp.plane_fmt[i].sizeimage) + return -EINVAL; + } + } else { + *nplanes = info->buffers; + for (i = 0; i < info->buffers; i++) + sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + return 0; +} + +static int xvip_m2m_buf_prepare(struct vb2_buffer *vb) +{ + struct xvip_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct xvip_m2m_dma *dma = ctx->xdev->dma; + struct v4l2_format *f; + const struct xvip_video_format *info; + u32 i; + + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + f = &dma->outfmt; + info = dma->outinfo; + } else { + f = &dma->capfmt; + info = dma->capinfo; + } + + for (i = 0; i < info->buffers; i++) { + if (vb2_plane_size(vb, i) < + f->fmt.pix_mp.plane_fmt[i].sizeimage) { + dev_err(ctx->xdev->dev, + "insufficient plane size (%u < %u)\n", + (u32)vb2_plane_size(vb, i), + f->fmt.pix_mp.plane_fmt[i].sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, i, + f->fmt.pix_mp.plane_fmt[i].sizeimage); + } + + return 0; +} + +static void xvip_m2m_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct xvip_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static void xvip_m2m_stop_streaming(struct vb2_queue *q) +{ + struct xvip_m2m_ctx *ctx = vb2_get_drv_priv(q); + struct xvip_m2m_dma *dma = ctx->xdev->dma; + struct xvip_pipeline *pipe = to_xvip_pipeline(&dma->video.entity); + struct vb2_v4l2_buffer *vbuf; + + dma->crop = false; + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + dmaengine_terminate_sync(dma->chan_tx); + else + dmaengine_terminate_sync(dma->chan_rx); + + if (ctx->xdev->num_subdevs) { + /* Stop the pipeline. */ + xvip_pipeline_set_stream(pipe, false); + + /* Cleanup the pipeline and mark it as being stopped. */ + xvip_pipeline_cleanup(pipe); + media_pipeline_stop(&dma->video.entity); + } + + for (;;) { + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + if (!vbuf) + return; + + spin_lock(&ctx->xdev->queued_lock); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + spin_unlock(&ctx->xdev->queued_lock); + } +} + +static int xvip_m2m_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct xvip_m2m_ctx *ctx = vb2_get_drv_priv(q); + struct xvip_m2m_dma *dma = ctx->xdev->dma; + struct xvip_m2m_dev *xdev = ctx->xdev; + struct xvip_pipeline *pipe; + int ret; + + if (!xdev->num_subdevs) + return 0; + + pipe = dma->video.entity.pipe + ? to_xvip_pipeline(&dma->video.entity) : &dma->pipe; + + ret = media_pipeline_start(&dma->video.entity, &pipe->pipe); + if (ret < 0) + goto error; + + /* Verify that the configured format matches the output of the + * connected subdev. + */ + ret = xvip_dma_verify_format(dma); + if (ret < 0) + goto error_stop; + + ret = xvip_pipeline_prepare(pipe, dma); + if (ret < 0) + goto error_stop; + + /* Start the pipeline. */ + ret = xvip_pipeline_set_stream(pipe, true); + if (ret < 0) + goto error_stop; + + return 0; +error_stop: + media_pipeline_stop(&dma->video.entity); + +error: + xvip_m2m_stop_streaming(q); + + return ret; +} + +static const struct vb2_ops m2m_vb2_ops = { + .queue_setup = xvip_m2m_queue_setup, + .buf_prepare = xvip_m2m_buf_prepare, + .buf_queue = xvip_m2m_buf_queue, + .start_streaming = xvip_m2m_start_streaming, + .stop_streaming = xvip_m2m_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int xvip_m2m_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct xvip_m2m_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &m2m_vb2_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->dev = ctx->xdev->v4l2_dev.dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &m2m_vb2_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->dev = ctx->xdev->v4l2_dev.dev; + + return vb2_queue_init(dst_vq); +} + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int +xvip_dma_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + strlcpy(cap->driver, XVIP_M2M_NAME, sizeof(cap->driver)); + strlcpy(cap->card, XVIP_M2M_NAME, sizeof(cap->card)); + strlcpy(cap->bus_info, XVIP_M2M_NAME, sizeof(cap->card)); + + return 0; +} + +static int +xvip_m2m_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct xvip_m2m_ctx *ctx = file2ctx(file); + struct xvip_m2m_dma *dma = ctx->xdev->dma; + const struct xvip_video_format *fmtinfo; + const struct xvip_video_format *fmt; + struct v4l2_subdev *subdev; + struct v4l2_subdev_format v4l_fmt; + struct xvip_m2m_dev *xdev = ctx->xdev; + u32 i, fmt_cnt, *fmts; + int ret; + + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + ret = xilinx_xdma_get_v4l2_vid_fmts(dma->chan_rx, + &fmt_cnt, &fmts); + else + ret = xilinx_xdma_get_v4l2_vid_fmts(dma->chan_tx, + &fmt_cnt, &fmts); + if (ret) + return ret; + + if (f->index >= fmt_cnt) + return -EINVAL; + + if (!xdev->num_subdevs) { + fmt = xvip_get_format_by_fourcc(fmts[f->index]); + if (IS_ERR(fmt)) + return PTR_ERR(fmt); + + f->pixelformat = fmt->fourcc; + strlcpy(f->description, fmt->description, + sizeof(f->description)); + return 0; + } + + if (f->index > 0) + return -EINVAL; + + /* Establish media pad format */ + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + subdev = xvip_dma_remote_subdev(&dma->pads[XVIP_PAD_SOURCE], + &v4l_fmt.pad); + else + subdev = xvip_dma_remote_subdev(&dma->pads[XVIP_PAD_SINK], + &v4l_fmt.pad); + if (!subdev) + return -EPIPE; + + v4l_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &v4l_fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + for (i = 0; i < fmt_cnt; i++) { + fmt = xvip_get_format_by_fourcc(fmts[i]); + if (IS_ERR(fmt)) + return PTR_ERR(fmt); + + if (fmt->code == v4l_fmt.format.code) + break; + } + + if (i >= fmt_cnt) + return -EINVAL; + + fmtinfo = xvip_get_format_by_fourcc(fmts[i]); + f->pixelformat = fmtinfo->fourcc; + strlcpy(f->description, fmtinfo->description, sizeof(f->description)); + + return 0; +} + +static int xvip_m2m_get_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct xvip_m2m_ctx *ctx = file2ctx(file); + struct xvip_m2m_dma *dma = ctx->xdev->dma; + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + f->fmt.pix_mp = dma->outfmt.fmt.pix_mp; + else + f->fmt.pix_mp = dma->capfmt.fmt.pix_mp; + + return 0; +} + +static int __xvip_m2m_try_fmt(struct xvip_m2m_ctx *ctx, struct v4l2_format *f) +{ + struct xvip_m2m_dma *dma = ctx->xdev->dma; + const struct xvip_video_format *info; + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_plane_pix_format *plane_fmt; + u32 align, min_width, max_width; + u32 bpl, min_bpl, max_bpl; + u32 padding_factor_nume, padding_factor_deno; + u32 bpl_nume, bpl_deno; + u32 i, plane_width, plane_height; + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + struct xvip_m2m_dev *xdev = ctx->xdev; + int ret; + + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + if (xdev->num_subdevs) { + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + subdev = xvip_dma_remote_subdev + (&dma->pads[XVIP_PAD_SOURCE], &fmt.pad); + else + subdev = xvip_dma_remote_subdev + (&dma->pads[XVIP_PAD_SINK], &fmt.pad); + + if (!subdev) + return -EPIPE; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return -EINVAL; + } + + pix_mp = &f->fmt.pix_mp; + plane_fmt = pix_mp->plane_fmt; + info = xvip_get_format_by_fourcc(f->fmt.pix_mp.pixelformat); + if (info) { + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + dma->outinfo = info; + else + dma->capinfo = info; + } else { + info = xvip_get_format_by_fourcc(XVIP_M2M_DEFAULT_FMT); + } + + if (xdev->num_subdevs) { + if (info->code != fmt.format.code || + fmt.format.width != pix_mp->width || + fmt.format.height != pix_mp->height) { + dev_err(xdev->dev, "Failed to set format\n"); + dev_info(xdev->dev, + "Reqed Code = %d, Width = %d, Height = %d\n", + info->code, pix_mp->width, pix_mp->height); + dev_info(xdev->dev, + "Subdev Code = %d, Width = %d, Height = %d", + fmt.format.code, fmt.format.width, + fmt.format.height); + return -EINVAL; + } + } + + xvip_width_padding_factor(info->fourcc, &padding_factor_nume, + &padding_factor_deno); + xvip_bpl_scaling_factor(info->fourcc, &bpl_nume, &bpl_deno); + + /* + * V4L2 specification suggests the driver corrects the format struct + * if any of the dimensions is unsupported + */ + align = lcm(dma->align, info->bpp >> 3); + min_width = roundup(XVIP_M2M_MIN_WIDTH, align); + max_width = rounddown(XVIP_M2M_MAX_WIDTH, align); + pix_mp->width = clamp(pix_mp->width, min_width, max_width); + pix_mp->height = clamp(pix_mp->height, XVIP_M2M_MIN_HEIGHT, + XVIP_M2M_MAX_HEIGHT); + + /* + * Clamp the requested bytes per line value. If the maximum + * bytes per line value is zero, the module doesn't support + * user configurable line sizes. Override the requested value + * with the minimum in that case. + */ + max_bpl = rounddown(XVIP_M2M_MAX_WIDTH, align); + + if (info->buffers == 1) { + /* Handling contiguous data with mplanes */ + min_bpl = (pix_mp->width * info->bpl_factor * + padding_factor_nume * bpl_nume) / + (padding_factor_deno * bpl_deno); + min_bpl = roundup(min_bpl, align); + bpl = roundup(plane_fmt[0].bytesperline, align); + plane_fmt[0].bytesperline = clamp(bpl, min_bpl, max_bpl); + + if (info->num_planes == 1) { + /* Single plane formats */ + plane_fmt[0].sizeimage = plane_fmt[0].bytesperline * + pix_mp->height; + } else { + /* Multi plane formats in contiguous buffer*/ + plane_fmt[0].sizeimage = + DIV_ROUND_UP(plane_fmt[0].bytesperline * + pix_mp->height * + info->bpp, 8); + } + } else { + /* Handling non-contiguous data with mplanes */ + for (i = 0; i < info->num_planes; i++) { + plane_width = pix_mp->width / (i ? info->hsub : 1); + plane_height = pix_mp->height / (i ? info->vsub : 1); + min_bpl = (plane_width * info->bpl_factor * + padding_factor_nume * bpl_nume) / + (padding_factor_deno * bpl_deno); + min_bpl = roundup(min_bpl, align); + bpl = rounddown(plane_fmt[i].bytesperline, align); + plane_fmt[i].bytesperline = clamp(bpl, min_bpl, + max_bpl); + plane_fmt[i].sizeimage = plane_fmt[i].bytesperline * + plane_height; + } + } + + return 0; +} + +static int xvip_m2m_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct xvip_m2m_ctx *ctx = file2ctx(file); + int ret; + + ret = __xvip_m2m_try_fmt(ctx, f); + if (ret < 0) + return ret; + + return 0; +} + +static int xvip_m2m_set_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct xvip_m2m_ctx *ctx = file2ctx(file); + struct xvip_m2m_dma *dma = ctx->xdev->dma; + struct vb2_queue *vq; + int ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->xdev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + ret = __xvip_m2m_try_fmt(ctx, f); + if (ret < 0) + return ret; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + dma->outfmt.fmt.pix_mp = f->fmt.pix_mp; + else + dma->capfmt.fmt.pix_mp = f->fmt.pix_mp; + + return 0; +} + +static int +xvip_m2m_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct xvip_m2m_ctx *ctx = file2ctx(file); + struct xvip_m2m_dma *dma = ctx->xdev->dma; + int ret = 0; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + ret = -ENOTTY; + break; + case V4L2_SEL_TGT_CROP: + s->r.left = 0; + s->r.top = 0; + s->r.width = dma->r.width; + s->r.height = dma->r.height; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int +xvip_m2m_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct xvip_m2m_ctx *ctx = file2ctx(file); + struct xvip_m2m_dma *dma = ctx->xdev->dma; + u32 min_width, max_width; + int ret = 0; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + ret = -ENOTTY; + break; + case V4L2_SEL_TGT_CROP: + if (s->r.width > dma->outfmt.fmt.pix_mp.width || + s->r.height > dma->outfmt.fmt.pix_mp.height || + s->r.top != 0 || s->r.left != 0) + return -EINVAL; + + dma->crop = true; + min_width = roundup(XVIP_M2M_MIN_WIDTH, dma->align); + max_width = rounddown(XVIP_M2M_MAX_WIDTH, dma->align); + dma->r.width = clamp(s->r.width, min_width, max_width); + dma->r.height = s->r.height; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ioctl_ops xvip_m2m_ioctl_ops = { + .vidioc_querycap = xvip_dma_querycap, + + .vidioc_enum_fmt_vid_cap_mplane = xvip_m2m_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = xvip_m2m_get_fmt, + .vidioc_try_fmt_vid_cap_mplane = xvip_m2m_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = xvip_m2m_set_fmt, + + .vidioc_enum_fmt_vid_out_mplane = xvip_m2m_enum_fmt, + .vidioc_g_fmt_vid_out_mplane = xvip_m2m_get_fmt, + .vidioc_try_fmt_vid_out_mplane = xvip_m2m_try_fmt, + .vidioc_s_fmt_vid_out_mplane = xvip_m2m_set_fmt, + .vidioc_s_selection = xvip_m2m_s_selection, + .vidioc_g_selection = xvip_m2m_g_selection, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, +}; + +/* + * File operations + */ +static int xvip_m2m_open(struct file *file) +{ + struct xvip_m2m_dev *xdev = video_drvdata(file); + struct xvip_m2m_ctx *ctx = NULL; + int ret; + + ctx = devm_kzalloc(xdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + ctx->xdev = xdev; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(xdev->m2m_dev, ctx, + &xvip_m2m_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + v4l2_fh_exit(&ctx->fh); + return ret; + } + + v4l2_fh_add(&ctx->fh); + dev_info(xdev->dev, "Created instance %p, m2m_ctx: %p\n", ctx, + ctx->fh.m2m_ctx); + return 0; +} + +static int xvip_m2m_release(struct file *file) +{ + struct xvip_m2m_ctx *ctx = file->private_data; + + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + return 0; +} + +static u32 xvip_m2m_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct xvip_m2m_ctx *ctx = file->private_data; + int ret; + + mutex_lock(&ctx->xdev->lock); + ret = v4l2_m2m_poll(file, ctx->fh.m2m_ctx, wait); + mutex_unlock(&ctx->xdev->lock); + + return ret; +} + +static int xvip_m2m_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct xvip_m2m_ctx *ctx = file->private_data; + + return v4l2_m2m_mmap(file, ctx->fh.m2m_ctx, vma); +} + +/* + * mem2mem callbacks + */ + +static int xvip_m2m_job_ready(void *priv) +{ + struct xvip_m2m_ctx *ctx = priv; + + if ((v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) && + (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) > 0)) + return 1; + + return 0; +} + +static void xvip_m2m_job_abort(void *priv) +{ + struct xvip_m2m_ctx *ctx = priv; + + /* Will cancel the transaction in the next interrupt handler */ + v4l2_m2m_job_finish(ctx->xdev->m2m_dev, ctx->fh.m2m_ctx); +} + +static void xvip_m2m_prep_submit_dev2mem_desc(struct xvip_m2m_ctx *ctx, + struct vb2_v4l2_buffer *dst_buf) +{ + struct xvip_m2m_dma *dma = ctx->xdev->dma; + struct xvip_m2m_dev *xdev = ctx->xdev; + struct dma_async_tx_descriptor *desc; + dma_addr_t p_out; + const struct xvip_video_format *info; + struct v4l2_pix_format_mplane *pix_mp; + u32 padding_factor_nume, padding_factor_deno; + u32 bpl_nume, bpl_deno; + u32 luma_size; + u32 flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; + enum operation_mode mode = DEFAULT; + + p_out = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + + if (!p_out) { + dev_err(xdev->dev, + "Acquiring kernel pointer to buffer failed\n"); + return; + } + + ctx->xt.dir = DMA_DEV_TO_MEM; + ctx->xt.src_sgl = false; + ctx->xt.dst_sgl = true; + ctx->xt.dst_start = p_out; + + pix_mp = &dma->capfmt.fmt.pix_mp; + info = dma->capinfo; + xilinx_xdma_set_mode(dma->chan_rx, mode); + xilinx_xdma_v4l2_config(dma->chan_rx, pix_mp->pixelformat); + xvip_width_padding_factor(pix_mp->pixelformat, &padding_factor_nume, + &padding_factor_deno); + xvip_bpl_scaling_factor(pix_mp->pixelformat, &bpl_nume, &bpl_deno); + + ctx->xt.frame_size = info->num_planes; + ctx->sgl[0].size = (pix_mp->width * info->bpl_factor * + padding_factor_nume * bpl_nume) / + (padding_factor_deno * bpl_deno); + ctx->sgl[0].icg = pix_mp->plane_fmt[0].bytesperline - ctx->sgl[0].size; + ctx->xt.numf = pix_mp->height; + + /* + * dst_icg is the number of bytes to jump after last luma addr + * and before first chroma addr + */ + ctx->sgl[0].src_icg = 0; + + if (info->buffers == 1) { + /* Handling contiguous data with mplanes */ + ctx->sgl[0].dst_icg = 0; + } else { + /* Handling non-contiguous data with mplanes */ + if (info->buffers == 2) { + dma_addr_t chroma_cap = + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 1); + luma_size = pix_mp->plane_fmt[0].bytesperline * + ctx->xt.numf; + if (chroma_cap > p_out) + ctx->sgl[0].dst_icg = chroma_cap - p_out - + luma_size; + } + } + + desc = dmaengine_prep_interleaved_dma(dma->chan_rx, &ctx->xt, flags); + if (!desc) { + dev_err(xdev->dev, "Failed to prepare DMA rx transfer\n"); + return; + } + + desc->callback = xvip_m2m_dma_callback; + desc->callback_param = ctx; + dmaengine_submit(desc); + dma_async_issue_pending(dma->chan_rx); +} + +static void xvip_m2m_prep_submit_mem2dev_desc(struct xvip_m2m_ctx *ctx, + struct vb2_v4l2_buffer *src_buf) +{ + struct xvip_m2m_dma *dma = ctx->xdev->dma; + struct xvip_m2m_dev *xdev = ctx->xdev; + struct dma_async_tx_descriptor *desc; + dma_addr_t p_in; + const struct xvip_video_format *info; + struct v4l2_pix_format_mplane *pix_mp; + u32 padding_factor_nume, padding_factor_deno; + u32 bpl_nume, bpl_deno; + u32 luma_size; + u32 flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; + enum operation_mode mode = DEFAULT; + u32 bpl, src_width, src_height; + + p_in = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + + if (!p_in) { + dev_err(xdev->dev, + "Acquiring kernel pointer to buffer failed\n"); + return; + } + + ctx->xt.dir = DMA_MEM_TO_DEV; + ctx->xt.src_sgl = true; + ctx->xt.dst_sgl = false; + ctx->xt.src_start = p_in; + + pix_mp = &dma->outfmt.fmt.pix_mp; + bpl = pix_mp->plane_fmt[0].bytesperline; + if (dma->crop) { + src_width = dma->r.width; + src_height = dma->r.height; + } else { + src_width = pix_mp->width; + src_height = pix_mp->height; + } + + info = dma->outinfo; + xilinx_xdma_set_mode(dma->chan_tx, mode); + xilinx_xdma_v4l2_config(dma->chan_tx, pix_mp->pixelformat); + xvip_width_padding_factor(pix_mp->pixelformat, &padding_factor_nume, + &padding_factor_deno); + xvip_bpl_scaling_factor(pix_mp->pixelformat, &bpl_nume, &bpl_deno); + + ctx->xt.frame_size = info->num_planes; + ctx->sgl[0].size = (src_width * info->bpl_factor * + padding_factor_nume * bpl_nume) / + (padding_factor_deno * bpl_deno); + ctx->sgl[0].icg = bpl - ctx->sgl[0].size; + ctx->xt.numf = src_height; + + /* + * src_icg is the number of bytes to jump after last luma addr + * and before first chroma addr + */ + ctx->sgl[0].dst_icg = 0; + + if (info->buffers == 1) { + /* Handling contiguous data with mplanes */ + ctx->sgl[0].src_icg = 0; + if (dma->crop) + ctx->sgl[0].src_icg = bpl * + (pix_mp->height - src_height); + } else { + /* Handling non-contiguous data with mplanes */ + if (info->buffers == 2) { + dma_addr_t chroma_out = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1); + luma_size = bpl * ctx->xt.numf; + if (chroma_out > p_in) + ctx->sgl[0].src_icg = chroma_out - p_in - + luma_size; + } + } + + desc = dmaengine_prep_interleaved_dma(dma->chan_tx, &ctx->xt, flags); + if (!desc) { + dev_err(xdev->dev, "Failed to prepare DMA tx transfer\n"); + return; + } + + desc->callback = xvip_m2m_dma_callback_mem2dev; + desc->callback_param = ctx; + dmaengine_submit(desc); + dma_async_issue_pending(dma->chan_tx); +} + +/** + * xvip_m2m_device_run - prepares and starts the device + * + * @priv: Instance private data + * + * This simulates all the immediate preparations required before starting + * a device. This will be called by the framework when it decides to schedule + * a particular instance. + */ +static void xvip_m2m_device_run(void *priv) +{ + struct xvip_m2m_ctx *ctx = priv; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + /* Prepare and submit mem2dev transaction */ + xvip_m2m_prep_submit_mem2dev_desc(ctx, src_buf); + + /* Prepare and submit dev2mem transaction */ + xvip_m2m_prep_submit_dev2mem_desc(ctx, dst_buf); +} + +static const struct v4l2_file_operations xvip_m2m_fops = { + .owner = THIS_MODULE, + .open = xvip_m2m_open, + .release = xvip_m2m_release, + .poll = xvip_m2m_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = xvip_m2m_mmap, +}; + +static struct video_device xvip_m2m_videodev = { + .name = XVIP_M2M_NAME, + .fops = &xvip_m2m_fops, + .ioctl_ops = &xvip_m2m_ioctl_ops, + .release = video_device_release_empty, + .vfl_dir = VFL_DIR_M2M, + .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, + .vfl_type = VFL_TYPE_GRABBER, +}; + +static const struct v4l2_m2m_ops xvip_m2m_ops = { + .device_run = xvip_m2m_device_run, + .job_ready = xvip_m2m_job_ready, + .job_abort = xvip_m2m_job_abort, +}; + +static int xvip_m2m_dma_init(struct xvip_m2m_dma *dma) +{ + struct xvip_m2m_dev *xdev; + struct v4l2_pix_format_mplane *pix_mp; + int ret; + + xdev = dma->xdev; + mutex_init(&xdev->lock); + mutex_init(&dma->pipe.lock); + spin_lock_init(&xdev->queued_lock); + + /* Format info on capture port - NV12 is the default format */ + dma->capinfo = xvip_get_format_by_fourcc(XVIP_M2M_DEFAULT_FMT); + pix_mp = &dma->capfmt.fmt.pix_mp; + pix_mp->pixelformat = dma->capinfo->fourcc; + + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->width = XVIP_M2M_DEF_WIDTH; + pix_mp->height = XVIP_M2M_DEF_HEIGHT; + pix_mp->plane_fmt[0].bytesperline = pix_mp->width * + dma->capinfo->bpl_factor; + pix_mp->plane_fmt[0].sizeimage = + DIV_ROUND_UP(pix_mp->plane_fmt[0].bytesperline * + pix_mp->height * dma->capinfo->bpp, 8); + + /* Format info on output port - NV12 is the default format */ + dma->outinfo = xvip_get_format_by_fourcc(XVIP_M2M_DEFAULT_FMT); + pix_mp = &dma->capfmt.fmt.pix_mp; + pix_mp->pixelformat = dma->outinfo->fourcc; + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->width = XVIP_M2M_DEF_WIDTH; + pix_mp->height = XVIP_M2M_DEF_HEIGHT; + pix_mp->plane_fmt[0].bytesperline = pix_mp->width * + dma->outinfo->bpl_factor; + pix_mp->plane_fmt[0].sizeimage = + DIV_ROUND_UP(pix_mp->plane_fmt[0].bytesperline * + pix_mp->height * dma->outinfo->bpp, 8); + + /* DMA channels for mem2mem */ + dma->chan_tx = dma_request_chan(xdev->dev, "tx"); + if (IS_ERR(dma->chan_tx)) { + ret = PTR_ERR(dma->chan_tx); + if (ret != -EPROBE_DEFER) + dev_err(xdev->dev, "mem2mem DMA tx channel not found"); + + return ret; + } + + dma->chan_rx = dma_request_chan(xdev->dev, "rx"); + if (IS_ERR(dma->chan_rx)) { + ret = PTR_ERR(dma->chan_rx); + if (ret != -EPROBE_DEFER) + dev_err(xdev->dev, "mem2mem DMA rx channel not found"); + + goto tx; + } + + dma->align = BIT(dma->chan_tx->device->copy_align); + + /* Video node */ + dma->video = xvip_m2m_videodev; + dma->video.v4l2_dev = &xdev->v4l2_dev; + dma->video.lock = &xdev->lock; + + dma->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + dma->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&dma->video.entity, 2, dma->pads); + if (ret < 0) + goto error; + + ret = video_register_device(&dma->video, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + dev_err(xdev->dev, "Failed to register mem2mem video device\n"); + goto tx_rx; + } + + video_set_drvdata(&dma->video, dma->xdev); + return 0; + +tx_rx: + dma_release_channel(dma->chan_rx); +tx: + dma_release_channel(dma->chan_tx); +error: + return ret; +} + +static void xvip_m2m_dma_deinit(struct xvip_m2m_dma *dma) +{ + if (video_is_registered(&dma->video)) + video_unregister_device(&dma->video); + + mutex_destroy(&dma->pipe.lock); + mutex_destroy(&dma->xdev->lock); + dma_release_channel(dma->chan_tx); + dma_release_channel(dma->chan_rx); +} + +static int xvip_m2m_dma_alloc_init(struct xvip_m2m_dev *xdev) +{ + struct xvip_m2m_dma *dma = NULL; + int ret; + + dma = devm_kzalloc(xdev->dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + dma->xdev = xdev; + xdev->dma = dma; + + ret = xvip_m2m_dma_init(xdev->dma); + if (ret) { + dev_err(xdev->dev, "DMA initialization failed\n"); + return ret; + } + + xdev->v4l2_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; + return 0; +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ +static void xvip_composite_v4l2_cleanup(struct xvip_m2m_dev *xdev) +{ + v4l2_device_unregister(&xdev->v4l2_dev); + media_device_unregister(&xdev->media_dev); + media_device_cleanup(&xdev->media_dev); +} + +static int xvip_composite_v4l2_init(struct xvip_m2m_dev *xdev) +{ + int ret; + + xdev->media_dev.dev = xdev->dev; + strlcpy(xdev->media_dev.model, "Xilinx Videoi M2M Composite Device", + sizeof(xdev->media_dev.model)); + xdev->media_dev.hw_revision = 0; + + media_device_init(&xdev->media_dev); + + xdev->v4l2_dev.mdev = &xdev->media_dev; + ret = v4l2_device_register(xdev->dev, &xdev->v4l2_dev); + if (ret < 0) { + dev_err(xdev->dev, "V4L2 device registration failed (%d)\n", + ret); + media_device_cleanup(&xdev->media_dev); + return ret; + } + + return 0; +} + +static struct xvip_graph_entity * +xvip_graph_find_entity(struct xvip_m2m_dev *xdev, + const struct device_node *node) +{ + struct xvip_graph_entity *entity; + + list_for_each_entry(entity, &xdev->entities, list) { + if (entity->node == node) + return entity; + } + + return NULL; +} + +static int xvip_graph_build_one(struct xvip_m2m_dev *xdev, + struct xvip_graph_entity *entity) +{ + u32 link_flags = MEDIA_LNK_FL_ENABLED; + struct media_entity *local = entity->entity; + struct media_entity *remote; + struct media_pad *local_pad; + struct media_pad *remote_pad; + struct xvip_graph_entity *ent; + struct v4l2_fwnode_link link; + struct device_node *ep = NULL; + struct device_node *next; + int ret = 0; + + dev_dbg(xdev->dev, "creating links for entity %s\n", local->name); + + while (1) { + /* Get the next endpoint and parse its link. */ + next = of_graph_get_next_endpoint(entity->node, ep); + if (!next) + break; + + ep = next; + + dev_dbg(xdev->dev, "processing endpoint %pOF\n", ep); + + ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); + if (ret < 0) { + dev_err(xdev->dev, "failed to parse link for %pOF\n", + ep); + continue; + } + + /* Skip sink ports, they will be processed from the other end of + * the link. + */ + if (link.local_port >= local->num_pads) { + dev_err(xdev->dev, "invalid port number %u for %pOF\n", + link.local_port, + to_of_node(link.local_node)); + v4l2_fwnode_put_link(&link); + ret = -EINVAL; + break; + } + + local_pad = &local->pads[link.local_port]; + + if (local_pad->flags & MEDIA_PAD_FL_SINK) { + dev_dbg(xdev->dev, "skipping sink port %pOF:%u\n", + to_of_node(link.local_node), + link.local_port); + v4l2_fwnode_put_link(&link); + continue; + } + + /* Skip DMA engines, they will be processed separately. */ + if (link.remote_node == of_fwnode_handle(xdev->dev->of_node)) { + dev_dbg(xdev->dev, "skipping DMA port %pOF:%u\n", + to_of_node(link.local_node), + link.local_port); + v4l2_fwnode_put_link(&link); + continue; + } + + /* Find the remote entity. */ + ent = xvip_graph_find_entity(xdev, + to_of_node(link.remote_node)); + if (!ent) { + dev_err(xdev->dev, "no entity found for %pOF\n", + to_of_node(link.remote_node)); + v4l2_fwnode_put_link(&link); + ret = -ENODEV; + break; + } + + remote = ent->entity; + + if (link.remote_port >= remote->num_pads) { + dev_err(xdev->dev, "invalid port number %u on %pOF\n", + link.remote_port, to_of_node(link.remote_node)); + v4l2_fwnode_put_link(&link); + ret = -EINVAL; + break; + } + + remote_pad = &remote->pads[link.remote_port]; + + v4l2_fwnode_put_link(&link); + + /* Create the media link. */ + dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", + local->name, local_pad->index, + remote->name, remote_pad->index); + + ret = media_create_pad_link(local, local_pad->index, + remote, remote_pad->index, + link_flags); + if (ret < 0) { + dev_err(xdev->dev, + "failed to create %s:%u -> %s:%u link\n", + local->name, local_pad->index, + remote->name, remote_pad->index); + break; + } + } + + return ret; +} + +static int xvip_graph_parse_one(struct xvip_m2m_dev *xdev, + struct device_node *node) +{ + struct xvip_graph_entity *entity; + struct device_node *remote; + struct device_node *ep = NULL; + int ret = 0; + + dev_dbg(xdev->dev, "parsing node %pOF\n", node); + + while (1) { + ep = of_graph_get_next_endpoint(node, ep); + if (!ep) + break; + + dev_dbg(xdev->dev, "handling endpoint %pOF %s\n", + ep, ep->name); + + remote = of_graph_get_remote_port_parent(ep); + if (!remote) { + ret = -EINVAL; + break; + } + dev_dbg(xdev->dev, "Remote endpoint %pOF %s\n", + remote, remote->name); + + /* Skip entities that we have already processed. */ + if (remote == xdev->dev->of_node || + xvip_graph_find_entity(xdev, remote)) { + of_node_put(remote); + continue; + } + + entity = devm_kzalloc(xdev->dev, sizeof(*entity), GFP_KERNEL); + if (!entity) { + of_node_put(remote); + ret = -ENOMEM; + break; + } + + entity->node = remote; + entity->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + entity->asd.match.fwnode = of_fwnode_handle(remote); + list_add_tail(&entity->list, &xdev->entities); + xdev->num_subdevs++; + } + + of_node_put(ep); + return ret; +} + +static int xvip_graph_parse(struct xvip_m2m_dev *xdev) +{ + struct xvip_graph_entity *entity; + int ret; + + /* + * Walk the links to parse the full graph. Start by parsing the + * composite node and then parse entities in turn. The list_for_each + * loop will handle entities added at the end of the list while walking + * the links. + */ + ret = xvip_graph_parse_one(xdev, xdev->dev->of_node); + if (ret < 0) + return 0; + + list_for_each_entry(entity, &xdev->entities, list) { + ret = xvip_graph_parse_one(xdev, entity->node); + if (ret < 0) + break; + } + + return ret; +} + +static int xvip_graph_build_dma(struct xvip_m2m_dev *xdev) +{ + u32 link_flags = MEDIA_LNK_FL_ENABLED; + struct device_node *node = xdev->dev->of_node; + struct media_entity *source; + struct media_entity *sink; + struct media_pad *source_pad; + struct media_pad *sink_pad; + struct xvip_graph_entity *ent; + struct v4l2_fwnode_link link; + struct device_node *ep = NULL; + struct device_node *next; + struct xvip_m2m_dma *dma = xdev->dma; + int ret = 0; + + dev_dbg(xdev->dev, "creating links for DMA engines\n"); + + while (1) { + /* Get the next endpoint and parse its link. */ + next = of_graph_get_next_endpoint(node, ep); + if (!next) + break; + + ep = next; + + dev_dbg(xdev->dev, "processing endpoint %pOF\n", ep); + + ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); + if (ret < 0) { + dev_err(xdev->dev, "failed to parse link for %pOF\n", + ep); + continue; + } + + dev_dbg(xdev->dev, "creating link for DMA engine %s\n", + dma->video.name); + + /* Find the remote entity. */ + ent = xvip_graph_find_entity(xdev, + to_of_node(link.remote_node)); + if (!ent) { + dev_err(xdev->dev, "no entity found for %pOF\n", + to_of_node(link.remote_node)); + v4l2_fwnode_put_link(&link); + ret = -ENODEV; + break; + } + if (link.remote_port >= ent->entity->num_pads) { + dev_err(xdev->dev, "invalid port number %u on %pOF\n", + link.remote_port, + to_of_node(link.remote_node)); + v4l2_fwnode_put_link(&link); + ret = -EINVAL; + break; + } + + dev_dbg(xdev->dev, "Entity %s %s\n", ent->node->name, + ent->node->full_name); + dev_dbg(xdev->dev, "port number %u on %pOF\n", + link.remote_port, to_of_node(link.remote_node)); + dev_dbg(xdev->dev, "local port number %u on %pOF\n", + link.local_port, to_of_node(link.local_node)); + + if (link.local_port == XVIP_PAD_SOURCE) { + source = &dma->video.entity; + source_pad = &dma->pads[XVIP_PAD_SOURCE]; + sink = ent->entity; + sink_pad = &sink->pads[XVIP_PAD_SINK]; + + } else { + source = ent->entity; + source_pad = &source->pads[XVIP_PAD_SOURCE]; + sink = &dma->video.entity; + sink_pad = &dma->pads[XVIP_PAD_SINK]; + } + + v4l2_fwnode_put_link(&link); + + /* Create the media link. */ + dev_dbg(xdev->dev, "creating %s:%u -> %s:%u link\n", + source->name, source_pad->index, + sink->name, sink_pad->index); + + ret = media_create_pad_link(source, source_pad->index, + sink, sink_pad->index, + link_flags); + if (ret < 0) { + dev_err(xdev->dev, + "failed to create %s:%u -> %s:%u link\n", + source->name, source_pad->index, + sink->name, sink_pad->index); + break; + } + } + + return ret; +} + +static int xvip_graph_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct xvip_m2m_dev *xdev = + container_of(notifier, struct xvip_m2m_dev, notifier); + struct xvip_graph_entity *entity; + int ret; + + dev_dbg(xdev->dev, "notify complete, all subdevs registered\n"); + + /* Create links for every entity. */ + list_for_each_entry(entity, &xdev->entities, list) { + ret = xvip_graph_build_one(xdev, entity); + if (ret < 0) + return ret; + } + + /* Create links for DMA channels. */ + ret = xvip_graph_build_dma(xdev); + if (ret < 0) + return ret; + + ret = v4l2_device_register_subdev_nodes(&xdev->v4l2_dev); + if (ret < 0) + dev_err(xdev->dev, "failed to register subdev nodes\n"); + + return media_device_register(&xdev->media_dev); +} + +static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct xvip_m2m_dev *xdev = + container_of(notifier, struct xvip_m2m_dev, notifier); + struct xvip_graph_entity *entity; + + /* Locate the entity corresponding to the bound subdev and store the + * subdev pointer. + */ + list_for_each_entry(entity, &xdev->entities, list) { + if (entity->node != subdev->dev->of_node) + continue; + + if (entity->subdev) { + dev_err(xdev->dev, "duplicate subdev for node %pOF\n", + entity->node); + return -EINVAL; + } + + dev_dbg(xdev->dev, "subdev %s bound\n", subdev->name); + entity->entity = &subdev->entity; + entity->subdev = subdev; + return 0; + } + + dev_err(xdev->dev, "no entity for subdev %s\n", subdev->name); + return -EINVAL; +} + +static const struct v4l2_async_notifier_operations xvip_graph_notify_ops = { + .bound = xvip_graph_notify_bound, + .complete = xvip_graph_notify_complete, +}; + +static void xvip_graph_cleanup(struct xvip_m2m_dev *xdev) +{ + struct xvip_graph_entity *entityp; + struct xvip_graph_entity *entity; + + v4l2_async_notifier_unregister(&xdev->notifier); + + list_for_each_entry_safe(entity, entityp, &xdev->entities, list) { + of_node_put(entity->node); + list_del(&entity->list); + } +} + +static int xvip_graph_init(struct xvip_m2m_dev *xdev) +{ + struct xvip_graph_entity *entity; + struct v4l2_async_subdev **subdevs = NULL; + unsigned int num_subdevs; + int ret; + + /* Init the DMA channels. */ + ret = xvip_m2m_dma_alloc_init(xdev); + if (ret < 0) { + dev_err(xdev->dev, "DMA initialization failed\n"); + goto done; + } + + /* Parse the graph to extract a list of subdevice DT nodes. */ + ret = xvip_graph_parse(xdev); + if (ret < 0) { + dev_err(xdev->dev, "graph parsing failed\n"); + goto done; + } + dev_dbg(xdev->dev, "Number of subdev = %d\n", xdev->num_subdevs); + + if (!xdev->num_subdevs) { + dev_err(xdev->dev, "no subdev found in graph\n"); + goto done; + } + + /* Register the subdevices notifier. */ + num_subdevs = xdev->num_subdevs; + subdevs = devm_kzalloc(xdev->dev, sizeof(*subdevs) * num_subdevs, + GFP_KERNEL); + if (!subdevs) { + ret = -ENOMEM; + goto done; + } + + v4l2_async_notifier_init(&xdev->notifier); + list_for_each_entry(entity, &xdev->entities, list) { + ret = v4l2_async_notifier_add_subdev(&xdev->notifier, &entity->asd); + if (ret) + goto done; + xdev->notifier.ops = &xvip_graph_notify_ops; + } + + ret = v4l2_async_notifier_register(&xdev->v4l2_dev, &xdev->notifier); + if (ret < 0) { + dev_err(xdev->dev, "notifier registration failed\n"); + goto done; + } + + ret = 0; + +done: + if (ret < 0) + xvip_graph_cleanup(xdev); + + return ret; +} + +static int xvip_composite_remove(struct platform_device *pdev) +{ + struct xvip_m2m_dev *xdev = platform_get_drvdata(pdev); + + xvip_graph_cleanup(xdev); + xvip_composite_v4l2_cleanup(xdev); + + return 0; +} + +static int xvip_m2m_probe(struct platform_device *pdev) +{ + struct xvip_m2m_dev *xdev = NULL; + int ret; + + xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL); + if (!xdev) + return -ENOMEM; + + xdev->dev = &pdev->dev; + INIT_LIST_HEAD(&xdev->entities); + + ret = xvip_composite_v4l2_init(xdev); + if (ret) + return -EINVAL; + + ret = xvip_graph_init(xdev); + if (ret < 0) + goto error; + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret); + goto dma_cleanup; + } + + platform_set_drvdata(pdev, xdev); + + xdev->m2m_dev = v4l2_m2m_init(&xvip_m2m_ops); + if (IS_ERR(xdev->m2m_dev)) { + dev_err(xdev->dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(xdev->m2m_dev); + goto dma_cleanup; + } + + dev_info(xdev->dev, "mem2mem device registered\n"); + return 0; + +dma_cleanup: + xvip_m2m_dma_deinit(xdev->dma); + +error: + v4l2_device_unregister(&xdev->v4l2_dev); + return ret; +} + +static int xvip_m2m_remove(struct platform_device *pdev) +{ + xvip_composite_remove(pdev); + return 0; +} + +static const struct of_device_id xvip_m2m_of_id_table[] = { + { .compatible = "xlnx,mem2mem" }, + { } +}; +MODULE_DEVICE_TABLE(of, xvip_m2m_of_id_table); + +static struct platform_driver xvip_m2m_driver = { + .driver = { + .name = XVIP_M2M_NAME, + .of_match_table = xvip_m2m_of_id_table, + }, + .probe = xvip_m2m_probe, + .remove = xvip_m2m_remove, +}; + +module_platform_driver(xvip_m2m_driver); + +MODULE_AUTHOR("Xilinx Inc."); +MODULE_DESCRIPTION("Xilinx V4L2 mem2mem driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-multi-scaler-coeff.h b/drivers/media/platform/xilinx/xilinx-multi-scaler-coeff.h new file mode 100644 index 000000000000..65a3482aa249 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-multi-scaler-coeff.h @@ -0,0 +1,574 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Xilinx Memory-to-Memory Video Multi-Scaler IP + * + * Copyright (C) 2018 Xilinx, Inc. + * + * Author: Suresh Gupta <sureshg@xilinx.com> + * + * The file contains the coefficients used by the Xilinx + * Video Multi Scaler Controller driver (xm2msc) + * + */ + +#define XSCALER_MAX_PHASES (64) +#define XSCALER_MAX_TAPS (12) + +#define XSCALER_TAPS_6 (6) +#define XSCALER_TAPS_8 (8) +#define XSCALER_TAPS_10 (10) +#define XSCALER_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, +}; + +/* H-scaler coefficients for 6, 8, 10 and 12 tap filters */ +static const short +xhsc_coeff_taps6[XSCALER_MAX_PHASES][XSCALER_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 short +xhsc_coeff_taps8[XSCALER_MAX_PHASES][XSCALER_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 short +xhsc_coeff_taps10[XSCALER_MAX_PHASES][XSCALER_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 short +xhsc_coeff_taps12[XSCALER_MAX_PHASES][XSCALER_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 short +xvsc_coeff_taps6[XSCALER_MAX_PHASES][XSCALER_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 short +xvsc_coeff_taps8[XSCALER_MAX_PHASES][XSCALER_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 short +xvsc_coeff_taps10[XSCALER_MAX_PHASES][XSCALER_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 short +xvsc_coeff_taps12[XSCALER_MAX_PHASES][XSCALER_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, }, +}; diff --git a/drivers/media/platform/xilinx/xilinx-multi-scaler.c b/drivers/media/platform/xilinx/xilinx-multi-scaler.c new file mode 100644 index 000000000000..2892ed5c223a --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-multi-scaler.c @@ -0,0 +1,2449 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx Memory-to-Memory Video Multi-Scaler IP + * + * Copyright (C) 2018 Xilinx, Inc. + * + * Author: Suresh Gupta <suresh.gupta@xilinx.com> + * + * Based on the virtual v4l2-mem2mem example device + * + * This driver adds support to control the Xilinx Video Multi + * Scaler Controller + */ + +#include <linux/clk.h> +#include <linux/delay.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/sched.h> +#include <linux/slab.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> + +#include "xilinx-multi-scaler-coeff.h" + +/* 0x0000 : Control signals */ +#define XM2MSC_AP_CTRL 0x0000 +#define XM2MSC_AP_CTRL_START BIT(0) +#define XM2MSC_AP_CTRL_DONE BIT(1) +#define XM2MSC_AP_CTRL_IDEL BIT(2) +#define XM2MSC_AP_CTRL_READY BIT(3) +#define XM2MSC_AP_CTRL_AUTO_RESTART BIT(7) + +/* 0x0004 : Global Interrupt Enable Register */ +#define XM2MSC_GIE 0x0004 +#define XM2MSC_GIE_EN BIT(0) + +/* 0x0008 : IP Interrupt Enable Register (Read/Write) */ +#define XM2MSC_IER 0x0008 +#define XM2MSC_ISR 0x000c +#define XM2MSC_ISR_DONE BIT(0) +#define XM2MSC_ISR_READY BIT(1) + +#define XM2MSC_NUM_OUTS 0x0010 + +#define XM2MSC_WIDTHIN 0x000 +#define XM2MSC_WIDTHOUT 0x008 +#define XM2MSC_HEIGHTIN 0x010 +#define XM2MSC_HEIGHTOUT 0x018 +#define XM2MSC_LINERATE 0x020 +#define XM2MSC_PIXELRATE 0x028 +#define XM2MSC_INPIXELFMT 0x030 +#define XM2MSC_OUTPIXELFMT 0x038 +#define XM2MSC_INSTRIDE 0x050 +#define XM2MSC_OUTSTRIDE 0x058 +#define XM2MSC_SRCIMGBUF0 0x060 +#define XM2MSC_SRCIMGBUF1 0x070 +#define XM2MSC_DSTIMGBUF0 0x090 +#define XM2MSC_DSTIMGBUF1 0x0100 + +#define XM2MVSC_VFLTCOEFF_L 0x2000 +#define XM2MVSC_VFLTCOEFF(x) (XM2MVSC_VFLTCOEFF_L + 0x2000 * (x)) +#define XM2MVSC_HFLTCOEFF_L 0x2800 +#define XM2MVSC_HFLTCOEFF(x) (XM2MVSC_HFLTCOEFF_L + 0x2000 * (x)) + +#define XM2MSC_CHAN_REGS_START(x) (0x100 + 0x200 * (x)) + +/* + * IP has reserved area between XM2MSC_DSTIMGBUF0 and + * XM2MSC_DSTIMGBUF1 registers of channel 4 + */ +#define XM2MSC_RESERVED_AREA 0x600 + +/* GPIO RESET MACROS */ +#define XM2MSC_RESET_ASSERT (0x1) +#define XM2MSC_RESET_DEASSERT (0x0) + +#define XM2MSC_MIN_CHAN 1 +#define XM2MSC_MAX_CHAN 8 + +#define XM2MSC_MAX_WIDTH (8192) +#define XM2MSC_MAX_HEIGHT (4320) +#define XM2MSC_MIN_WIDTH (64) +#define XM2MSC_MIN_HEIGHT (64) +#define XM2MSC_STEP_PRECISION (65536) +/* Mask definitions for Low 16 bits in a 32 bit number */ +#define XM2MSC_MASK_LOW_16BITS GENMASK(15, 0) +#define XM2MSC_BITSHIFT_16 (16) + +#define XM2MSC_DRIVER_NAME "xm2msc" + +#define CHAN_ATTACHED BIT(0) +#define CHAN_OPENED BIT(1) + +#define XM2MSC_CHAN_OUT 0 +#define XM2MSC_CHAN_CAP 1 + +#define NUM_STREAM(_x) \ + ({ typeof(_x) (x) = (_x); \ + min(ffz(x->out_streamed_chan), \ + ffz(x->cap_streamed_chan)); }) + +#define XM2MSC_ALIGN_MUL 8 + +/* + * These are temporary variables. Once the stride and height + * alignment support added to plugin, these variables will + * be remove. + */ +static unsigned int output_stride_align[XM2MSC_MAX_CHAN] = { + 1, 1, 1, 1, 1, 1, 1, 1 }; +module_param_array(output_stride_align, uint, NULL, 0644); +MODULE_PARM_DESC(output_stride_align, + "Per Cahnnel stride alignment requied at output."); + +static unsigned int capture_stride_align[XM2MSC_MAX_CHAN] = { + 1, 1, 1, 1, 1, 1, 1, 1 }; +module_param_array(capture_stride_align, uint, NULL, 0644); +MODULE_PARM_DESC(capture_stride_align, + "Per channel stride alignment requied at capture."); + +static unsigned int output_height_align[XM2MSC_MAX_CHAN] = { + 1, 1, 1, 1, 1, 1, 1, 1 }; +module_param_array(output_height_align, uint, NULL, 0644); +MODULE_PARM_DESC(output_height_align, + "Per Channel height alignment requied at output."); + +static unsigned int capture_height_align[XM2MSC_MAX_CHAN] = { + 1, 1, 1, 1, 1, 1, 1, 1 }; +module_param_array(capture_height_align, uint, NULL, 0644); +MODULE_PARM_DESC(capture_height_align, + "Per channel height alignment requied at capture."); + +/* Xilinx Video Specific Color/Pixel Formats */ +enum xm2msc_pix_fmt { + XILINX_M2MSC_FMT_RGBX8 = 10, + XILINX_M2MSC_FMT_YUVX8 = 11, + XILINX_M2MSC_FMT_YUYV8 = 12, + XILINX_M2MSC_FMT_RGBX10 = 15, + XILINX_M2MSC_FMT_YUVX10 = 16, + XILINX_M2MSC_FMT_Y_UV8 = 18, + XILINX_M2MSC_FMT_Y_UV8_420 = 19, + XILINX_M2MSC_FMT_RGB8 = 20, + XILINX_M2MSC_FMT_YUV8 = 21, + XILINX_M2MSC_FMT_Y_UV10 = 22, + XILINX_M2MSC_FMT_Y_UV10_420 = 23, + XILINX_M2MSC_FMT_Y8 = 24, + XILINX_M2MSC_FMT_Y10 = 25, + XILINX_M2MSC_FMT_BGRX8 = 27, + XILINX_M2MSC_FMT_UYVY8 = 28, + XILINX_M2MSC_FMT_BGR8 = 29, +}; + +/** + * struct xm2msc_fmt - driver info for each of the supported video formats + * @name: human-readable device tree name for this entry + * @fourcc: standard format identifier + * @xm2msc_fmt: Xilinx Video Specific Color/Pixel Formats + * @num_buffs: number of physically non-contiguous data planes/buffs + */ +struct xm2msc_fmt { + char *name; + u32 fourcc; + enum xm2msc_pix_fmt xm2msc_fmt; + u32 num_buffs; +}; + +static const struct xm2msc_fmt formats[] = { + { + .name = "xbgr8888", + .fourcc = V4L2_PIX_FMT_BGRX32, + .xm2msc_fmt = XILINX_M2MSC_FMT_RGBX8, + .num_buffs = 1, + }, + { + .name = "xvuy8888", + .fourcc = V4L2_PIX_FMT_XVUY32, + .xm2msc_fmt = XILINX_M2MSC_FMT_YUVX8, + .num_buffs = 1, + }, + { + .name = "yuyv", + .fourcc = V4L2_PIX_FMT_YUYV, + .xm2msc_fmt = XILINX_M2MSC_FMT_YUYV8, + .num_buffs = 1, + }, + { + .name = "xbgr2101010", + .fourcc = V4L2_PIX_FMT_XBGR30, + .xm2msc_fmt = XILINX_M2MSC_FMT_RGBX10, + .num_buffs = 1, + }, + { + .name = "yuvx2101010", + .fourcc = V4L2_PIX_FMT_XVUY10, + .xm2msc_fmt = XILINX_M2MSC_FMT_YUVX10, + .num_buffs = 1, + }, + { + .name = "nv16", + .fourcc = V4L2_PIX_FMT_NV16M, + .xm2msc_fmt = XILINX_M2MSC_FMT_Y_UV8, + .num_buffs = 2, + }, + { + .name = "nv16", + .fourcc = V4L2_PIX_FMT_NV16, + .xm2msc_fmt = XILINX_M2MSC_FMT_Y_UV8, + .num_buffs = 1, + }, + { + .name = "nv12", + .fourcc = V4L2_PIX_FMT_NV12M, + .xm2msc_fmt = XILINX_M2MSC_FMT_Y_UV8_420, + .num_buffs = 2, + }, + { + .name = "nv12", + .fourcc = V4L2_PIX_FMT_NV12, + .xm2msc_fmt = XILINX_M2MSC_FMT_Y_UV8_420, + .num_buffs = 1, + }, + { + .name = "bgr888", + .fourcc = V4L2_PIX_FMT_RGB24, + .xm2msc_fmt = XILINX_M2MSC_FMT_RGB8, + .num_buffs = 1, + }, + { + .name = "vuy888", + .fourcc = V4L2_PIX_FMT_VUY24, + .xm2msc_fmt = XILINX_M2MSC_FMT_YUV8, + .num_buffs = 1, + }, + { + .name = "xv20", + .fourcc = V4L2_PIX_FMT_XV20M, + .xm2msc_fmt = XILINX_M2MSC_FMT_Y_UV10, + .num_buffs = 2, + }, + { + .name = "xv20", + .fourcc = V4L2_PIX_FMT_XV20, + .xm2msc_fmt = XILINX_M2MSC_FMT_Y_UV10, + .num_buffs = 1, + }, + { + .name = "xv15", + .fourcc = V4L2_PIX_FMT_XV15M, + .xm2msc_fmt = XILINX_M2MSC_FMT_Y_UV10_420, + .num_buffs = 2, + }, + { + .name = "xv15", + .fourcc = V4L2_PIX_FMT_XV15, + .xm2msc_fmt = XILINX_M2MSC_FMT_Y_UV10_420, + .num_buffs = 1, + }, + { + .name = "y8", + .fourcc = V4L2_PIX_FMT_GREY, + .xm2msc_fmt = XILINX_M2MSC_FMT_Y8, + .num_buffs = 1, + }, + { + .name = "y10", + .fourcc = V4L2_PIX_FMT_Y10, + .xm2msc_fmt = XILINX_M2MSC_FMT_Y10, + .num_buffs = 1, + }, + { + .name = "xrgb8888", + .fourcc = V4L2_PIX_FMT_XBGR32, + .xm2msc_fmt = XILINX_M2MSC_FMT_BGRX8, + .num_buffs = 1, + }, + { + .name = "uyvy", + .fourcc = V4L2_PIX_FMT_UYVY, + .xm2msc_fmt = XILINX_M2MSC_FMT_UYVY8, + .num_buffs = 1, + }, + { + .name = "rgb888", + .fourcc = V4L2_PIX_FMT_BGR24, + .xm2msc_fmt = XILINX_M2MSC_FMT_BGR8, + .num_buffs = 1, + }, +}; + +/** + * struct xm2msc_q_data - Per-queue, driver-specific private data + * There is one source queue and one destination queue for each m2m context. + * @width: frame width + * @height: frame height + * @stride: bytes per lines + * @nbuffs: Current number of buffs + * @bytesperline: bytes per line per plane + * @sizeimage: image size per plane + * @colorspace: supported colorspace + * @field: supported field value + * @fmt: format info + */ +struct xm2msc_q_data { + unsigned int width; + unsigned int height; + unsigned int stride; + unsigned int nbuffs; + unsigned int bytesperline[2]; + unsigned int sizeimage[2]; + enum v4l2_colorspace colorspace; + enum v4l2_field field; + const struct xm2msc_fmt *fmt; +}; + +/** + * struct xm2msc_chan_ctx - Scaler Channel Info, Per-Channel context + * @regs: IO mapped base address of the Channel + * @xm2msc_dev: Pointer to struct xm2m_msc_dev + * @num: HW Scaling Channel number + * @minor: Minor number of the video device + * @output_stride_align: required align stride value at output pad + * @capture_stride_align: required align stride valure at capture pad + * @output_height_align: required align height value at output pad + * @capture_height_align: required align heigh value at capture pad + * @status: channel status, CHAN_ATTACHED or CHAN_OPENED + * @frames: number of frames processed + * @vfd: V4L2 device + * @fh: v4l2 file handle + * @m2m_dev: m2m device + * @m2m_ctx: memory to memory context structure + * @q_data: src & dst queue data + */ +struct xm2msc_chan_ctx { + void __iomem *regs; + struct xm2m_msc_dev *xm2msc_dev; + u32 num; + u32 minor; + u32 output_stride_align; + u32 capture_stride_align; + u32 output_height_align; + u32 capture_height_align; + u8 status; + unsigned long frames; + + struct video_device vfd; + struct v4l2_fh fh; + struct v4l2_m2m_dev *m2m_dev; + struct v4l2_m2m_ctx *m2m_ctx; + + struct xm2msc_q_data q_data[2]; +}; + +/** + * struct xm2m_msc_dev - Xilinx M2M Multi-scaler Device + * @dev: pointer to struct device instance used by the driver + * @regs: IO mapped base address of the HW/IP + * @irq: interrupt number + * @clk: video core clock + * @max_chan: maximum number of Scaling Channels + * @max_ht: maximum number of rows in a plane + * @max_wd: maximum number of column in a plane + * @taps: number of taps set in HW + * @supported_fmt: bitmap for all supported fmts by HW + * @dma_addr_size: Size of dma address pointer in IP (either 32 or 64) + * @ppc: Pixels per clock set in IP (1, 2 or 4) + * @rst_gpio: reset gpio handler + * @opened_chan: bitmap for all open channel + * @out_streamed_chan: bitmap for all out streamed channel + * @cap_streamed_chan: bitmap for all capture streamed channel + * @running_chan: currently running channels + * @device_busy: HW device is busy or not + * @isr_wait: flag to follow the ISR complete or not + * @isr_finished: Wait queue used to wait for IP to complete processing + * @v4l2_dev: main struct to for V4L2 device drivers + * @dev_mutex: lock for V4L2 device + * @mutex: lock for channel ctx + * @lock: lock used in IRQ + * @xm2msc_chan: arrey of channel context + * @hscaler_coeff: Array of filter coefficients for the Horizontal Scaler + * @vscaler_coeff: Array of filter coefficients for the Vertical Scaler + */ +struct xm2m_msc_dev { + struct device *dev; + void __iomem *regs; + int irq; + struct clk *clk; + u32 max_chan; + u32 max_ht; + u32 max_wd; + u32 taps; + u32 supported_fmt; + u32 dma_addr_size; + u8 ppc; + struct gpio_desc *rst_gpio; + + u32 opened_chan; + u32 out_streamed_chan; + u32 cap_streamed_chan; + u32 running_chan; + bool device_busy; + bool isr_wait; + wait_queue_head_t isr_finished; + + struct v4l2_device v4l2_dev; + + struct mutex dev_mutex; /*the mutex for v4l2*/ + struct mutex mutex; /*lock for bitmap reg*/ + spinlock_t lock; /*IRQ lock*/ + + struct xm2msc_chan_ctx xm2msc_chan[XM2MSC_MAX_CHAN]; + short hscaler_coeff[XSCALER_MAX_PHASES][XSCALER_MAX_TAPS]; + short vscaler_coeff[XSCALER_MAX_PHASES][XSCALER_MAX_TAPS]; +}; + +#define fh_to_chanctx(__fh) container_of(__fh, struct xm2msc_chan_ctx, fh) + +static inline u32 xm2msc_readreg(const void __iomem *addr) +{ + return ioread32(addr); +} + +static inline void xm2msc_write64reg(void __iomem *addr, u64 value) +{ + iowrite32(lower_32_bits(value), addr); + iowrite32(upper_32_bits(value), (void __iomem *)(addr + 4)); +} + +static inline void xm2msc_writereg(void __iomem *addr, u32 value) +{ + iowrite32(value, addr); +} + +static bool xm2msc_is_yuv_singlebuff(u32 fourcc) +{ + if (fourcc == V4L2_PIX_FMT_NV12 || fourcc == V4L2_PIX_FMT_XV15 || + fourcc == V4L2_PIX_FMT_NV16 || fourcc == V4L2_PIX_FMT_XV20) + return true; + + return false; +} + +static inline u32 xm2msc_yuv_1stplane_size(struct xm2msc_q_data *q_data, + u32 row_align) +{ + return q_data->bytesperline[0] * ALIGN(q_data->height, row_align); +} + +static struct xm2msc_q_data *get_q_data(struct xm2msc_chan_ctx *chan_ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &chan_ctx->q_data[XM2MSC_CHAN_OUT]; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &chan_ctx->q_data[XM2MSC_CHAN_CAP]; + default: + v4l2_err(&chan_ctx->xm2msc_dev->v4l2_dev, + "Not supported Q type %d\n", type); + } + return NULL; +} + +static u32 find_format_index(struct v4l2_format *f) +{ + const struct xm2msc_fmt *fmt; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + fmt = &formats[i]; + if (fmt->fourcc == f->fmt.pix_mp.pixelformat) + break; + } + + return i; +} + +static const struct xm2msc_fmt *find_format(struct v4l2_format *f) +{ + const struct xm2msc_fmt *fmt; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + fmt = &formats[i]; + if (fmt->fourcc == f->fmt.pix_mp.pixelformat) + break; + } + + if (i == ARRAY_SIZE(formats)) + return NULL; + + return &formats[i]; +} + +static void +xm2msc_hscaler_load_ext_coeff(struct xm2m_msc_dev *xm2msc, + 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 = XSCALER_MAX_TAPS - ntaps; + offset = pad >> 1; + + memset(xm2msc->hscaler_coeff, 0, sizeof(xm2msc->hscaler_coeff)); + + /* Load coefficients into scaler coefficient table */ + for (i = 0; i < nphases; i++) { + for (j = 0; j < ntaps; ++j) + xm2msc->hscaler_coeff[i][j + offset] = + coeff[i * ntaps + j]; + } +} + +static void xm2msc_hscaler_set_coeff(struct xm2msc_chan_ctx *chan_ctx, + const u32 base_addr) +{ + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + int val, offset, rd_indx; + unsigned int i, j; + u32 ntaps = chan_ctx->xm2msc_dev->taps; + const u32 nphases = XSCALER_MAX_PHASES; + + offset = (XSCALER_MAX_TAPS - ntaps) / 2; + for (i = 0; i < nphases; i++) { + for (j = 0; j < ntaps / 2; j++) { + rd_indx = j * 2 + offset; + val = (xm2msc->hscaler_coeff[i][rd_indx + 1] << + XM2MSC_BITSHIFT_16) | + (xm2msc->hscaler_coeff[i][rd_indx] & + XM2MSC_MASK_LOW_16BITS); + xm2msc_writereg((xm2msc->regs + base_addr) + + ((i * ntaps / 2 + j) * 4), val); + } + } +} + +static void +xm2msc_vscaler_load_ext_coeff(struct xm2m_msc_dev *xm2msc, + const short *coeff, const u32 ntaps) +{ + unsigned int i, j; + int pad, offset; + const u32 nphases = XSCALER_MAX_PHASES; + + /* Determine if coefficient needs padding (effective vs. max taps) */ + pad = XSCALER_MAX_TAPS - ntaps; + offset = pad ? (pad >> 1) : 0; + + /* Zero Entire Array */ + memset(xm2msc->vscaler_coeff, 0, sizeof(xm2msc->vscaler_coeff)); + + /* Load User defined coefficients into scaler coefficient table */ + for (i = 0; i < nphases; i++) { + for (j = 0; j < ntaps; ++j) + xm2msc->vscaler_coeff[i][j + offset] = + coeff[i * ntaps + j]; + } +} + +static void +xm2msc_vscaler_set_coeff(struct xm2msc_chan_ctx *chan_ctx, + const u32 base_addr) +{ + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + u32 val, i, j, offset, rd_indx; + u32 ntaps = chan_ctx->xm2msc_dev->taps; + const u32 nphases = XSCALER_MAX_PHASES; + + offset = (XSCALER_MAX_TAPS - ntaps) / 2; + + for (i = 0; i < nphases; i++) { + for (j = 0; j < ntaps / 2; j++) { + rd_indx = j * 2 + offset; + val = (xm2msc->vscaler_coeff[i][rd_indx + 1] << + XM2MSC_BITSHIFT_16) | + (xm2msc->vscaler_coeff[i][rd_indx] & + XM2MSC_MASK_LOW_16BITS); + xm2msc_writereg((xm2msc->regs + + base_addr) + ((i * ntaps / 2 + j) * 4), val); + } + } +} + +static u32 +xm2msc_select_hcoeff(struct xm2msc_chan_ctx *chan_ctx, const short **coeff) +{ + u16 hscale_ratio; + u32 width_in = chan_ctx->q_data[XM2MSC_CHAN_OUT].width; + u32 width_out = chan_ctx->q_data[XM2MSC_CHAN_CAP].width; + u32 ntaps = chan_ctx->xm2msc_dev->taps; + + if (width_out < width_in) { + hscale_ratio = (width_in * 10) / width_out; + + switch (chan_ctx->xm2msc_dev->taps) { + case XSCALER_TAPS_12: + if (hscale_ratio > 35) { + *coeff = &xhsc_coeff_taps12[0][0]; + ntaps = XSCALER_TAPS_12; + } else if (hscale_ratio > 25) { + *coeff = &xhsc_coeff_taps10[0][0]; + ntaps = XSCALER_TAPS_10; + } else if (hscale_ratio > 15) { + *coeff = &xhsc_coeff_taps8[0][0]; + ntaps = XSCALER_TAPS_8; + } else { + *coeff = &xhsc_coeff_taps6[0][0]; + ntaps = XSCALER_TAPS_6; + } + break; + case XSCALER_TAPS_10: + if (hscale_ratio > 25) { + *coeff = &xhsc_coeff_taps10[0][0]; + ntaps = XSCALER_TAPS_10; + } else if (hscale_ratio > 15) { + *coeff = &xhsc_coeff_taps8[0][0]; + ntaps = XSCALER_TAPS_8; + } else { + *coeff = &xhsc_coeff_taps6[0][0]; + ntaps = XSCALER_TAPS_6; + } + break; + case XSCALER_TAPS_8: + if (hscale_ratio > 15) { + *coeff = &xhsc_coeff_taps8[0][0]; + ntaps = XSCALER_TAPS_8; + } else { + *coeff = &xhsc_coeff_taps6[0][0]; + ntaps = XSCALER_TAPS_6; + } + break; + default: /* or XSCALER_TAPS_6 */ + *coeff = &xhsc_coeff_taps6[0][0]; + ntaps = XSCALER_TAPS_6; + } + } else { + /* + * Scale Up Mode will always use 6 tap filter + * This also includes 1:1 + */ + *coeff = &xhsc_coeff_taps6[0][0]; + ntaps = XSCALER_TAPS_6; + } + + return ntaps; +} + +static u32 +xm2msc_select_vcoeff(struct xm2msc_chan_ctx *chan_ctx, const short **coeff) +{ + u16 vscale_ratio; + u32 height_in = chan_ctx->q_data[XM2MSC_CHAN_OUT].height; + u32 height_out = chan_ctx->q_data[XM2MSC_CHAN_CAP].height; + u32 ntaps = chan_ctx->xm2msc_dev->taps; + + if (height_out < height_in) { + vscale_ratio = (height_in * 10) / height_out; + + switch (chan_ctx->xm2msc_dev->taps) { + case XSCALER_TAPS_12: + if (vscale_ratio > 35) { + *coeff = &xvsc_coeff_taps12[0][0]; + ntaps = XSCALER_TAPS_12; + } else if (vscale_ratio > 25) { + *coeff = &xvsc_coeff_taps10[0][0]; + ntaps = XSCALER_TAPS_10; + } else if (vscale_ratio > 15) { + *coeff = &xvsc_coeff_taps8[0][0]; + ntaps = XSCALER_TAPS_8; + } else { + *coeff = &xvsc_coeff_taps6[0][0]; + ntaps = XSCALER_TAPS_6; + } + break; + case XSCALER_TAPS_10: + if (vscale_ratio > 25) { + *coeff = &xvsc_coeff_taps10[0][0]; + ntaps = XSCALER_TAPS_10; + } else if (vscale_ratio > 15) { + *coeff = &xvsc_coeff_taps8[0][0]; + ntaps = XSCALER_TAPS_8; + } else { + *coeff = &xvsc_coeff_taps6[0][0]; + ntaps = XSCALER_TAPS_6; + } + break; + case XSCALER_TAPS_8: + if (vscale_ratio > 15) { + *coeff = &xvsc_coeff_taps8[0][0]; + ntaps = XSCALER_TAPS_8; + } else { + *coeff = &xvsc_coeff_taps6[0][0]; + ntaps = XSCALER_TAPS_6; + } + break; + default: /* or XSCALER_TAPS_6 */ + *coeff = &xvsc_coeff_taps6[0][0]; + ntaps = XSCALER_TAPS_6; + } + } else { + /* + * Scale Up Mode will always use 6 tap filter + * This also includes 1:1 + */ + *coeff = &xvsc_coeff_taps6[0][0]; + ntaps = XSCALER_TAPS_6; + } + + return ntaps; +} + +static void xm2mvsc_initialize_coeff_banks(struct xm2msc_chan_ctx *chan_ctx) +{ + const short *coeff = NULL; + u32 ntaps; + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + + ntaps = xm2msc_select_hcoeff(chan_ctx, &coeff); + xm2msc_hscaler_load_ext_coeff(xm2msc, coeff, ntaps); + xm2msc_hscaler_set_coeff(chan_ctx, XM2MVSC_HFLTCOEFF(chan_ctx->num)); + + dev_dbg(xm2msc->dev, "htaps %d selected for chan %d\n", + ntaps, chan_ctx->num); + + ntaps = xm2msc_select_vcoeff(chan_ctx, &coeff); + xm2msc_vscaler_load_ext_coeff(xm2msc, coeff, ntaps); + xm2msc_vscaler_set_coeff(chan_ctx, XM2MVSC_VFLTCOEFF(chan_ctx->num)); + + dev_dbg(xm2msc->dev, "vtaps %d selected for chan %d\n", + ntaps, chan_ctx->num); +} + +static void xm2msc_set_chan_params(struct xm2msc_chan_ctx *chan_ctx, + enum v4l2_buf_type type) +{ + struct xm2msc_q_data *q_data = get_q_data(chan_ctx, type); + const struct xm2msc_fmt *fmt = q_data->fmt; + void __iomem *base = chan_ctx->regs; + + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + xm2msc_writereg(base + XM2MSC_WIDTHIN, q_data->width); + xm2msc_writereg(base + XM2MSC_HEIGHTIN, q_data->height); + xm2msc_writereg(base + XM2MSC_INPIXELFMT, fmt->xm2msc_fmt); + xm2msc_writereg(base + XM2MSC_INSTRIDE, q_data->stride); + } else { + xm2msc_writereg(base + XM2MSC_WIDTHOUT, q_data->width); + xm2msc_writereg(base + XM2MSC_HEIGHTOUT, q_data->height); + xm2msc_writereg(base + XM2MSC_OUTPIXELFMT, fmt->xm2msc_fmt); + xm2msc_writereg(base + XM2MSC_OUTSTRIDE, q_data->stride); + } +} + +static void xm2msc_set_chan_com_params(struct xm2msc_chan_ctx *chan_ctx) +{ + void __iomem *base = chan_ctx->regs; + struct xm2msc_q_data *out_q_data = &chan_ctx->q_data[XM2MSC_CHAN_OUT]; + struct xm2msc_q_data *cap_q_data = &chan_ctx->q_data[XM2MSC_CHAN_CAP]; + u32 pixel_rate; + u32 line_rate; + + xm2mvsc_initialize_coeff_banks(chan_ctx); + + pixel_rate = (out_q_data->width * XM2MSC_STEP_PRECISION) / + cap_q_data->width; + line_rate = (out_q_data->height * XM2MSC_STEP_PRECISION) / + cap_q_data->height; + + xm2msc_writereg(base + XM2MSC_PIXELRATE, pixel_rate); + xm2msc_writereg(base + XM2MSC_LINERATE, line_rate); +} + +static void xm2msc_program_allchan(struct xm2m_msc_dev *xm2msc) +{ + u32 chan; + + for (chan = 0; chan < xm2msc->running_chan; chan++) { + struct xm2msc_chan_ctx *chan_ctx; + + chan_ctx = &xm2msc->xm2msc_chan[chan]; + + xm2msc_set_chan_params(chan_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + xm2msc_set_chan_params(chan_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + xm2msc_set_chan_com_params(chan_ctx); + } +} + +static void +xm2msc_pr_q(struct device *dev, struct xm2msc_q_data *q, int chan, + int type, const char *fun_name) +{ + unsigned int i; + const struct xm2msc_fmt *fmt = q->fmt; + + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + dev_dbg(dev, "\n\nOUTPUT Q (%d) Context from [[ %s ]]", + chan, fun_name); + else + dev_dbg(dev, "\n\nCAPTURE Q (%d) Context from [[ %s ]]", + chan, fun_name); + + dev_dbg(dev, "width height stride clrspace field planes\n"); + dev_dbg(dev, " %d %d %d %d %d %d\n", + q->width, q->height, q->stride, + q->colorspace, q->field, q->nbuffs); + + for (i = 0; i < q->nbuffs; i++) { + dev_dbg(dev, "[plane %d ] bytesperline sizeimage\n", i); + dev_dbg(dev, " %d %d\n", + q->bytesperline[i], q->sizeimage[i]); + } + + dev_dbg(dev, "fmt_name 4cc xlnx-fmt\n"); + dev_dbg(dev, "%s %d %d\n", + fmt->name, fmt->fourcc, fmt->xm2msc_fmt); + dev_dbg(dev, "\n\n"); +} + +static void +xm2msc_pr_status(struct xm2m_msc_dev *xm2msc, + const char *fun_name) +{ + struct device *dev = xm2msc->dev; + + dev_dbg(dev, "Status in %s\n", fun_name); + dev_dbg(dev, "opened_chan out_streamed_chan cap_streamed_chan\n"); + dev_dbg(dev, "0x%x 0x%x 0x%x\n", + xm2msc->opened_chan, xm2msc->out_streamed_chan, + xm2msc->cap_streamed_chan); + dev_dbg(dev, "\n\n"); +} + +static void +xm2msc_pr_chanctx(struct xm2msc_chan_ctx *ctx, const char *fun_name) +{ + struct device *dev = ctx->xm2msc_dev->dev; + + dev_dbg(dev, "\n\n----- [[ %s ]]: Channel %d (0x%p) context -----\n", + fun_name, ctx->num, ctx); + dev_dbg(dev, "minor = %d\n", ctx->minor); + dev_dbg(dev, "reg mapped at %p\n", ctx->regs); + dev_dbg(dev, "xm2msc \tm2m_dev \tm2m_ctx\n"); + dev_dbg(dev, "%p \t%p \t%p\n", ctx->xm2msc_dev, + ctx->m2m_dev, ctx->m2m_ctx); + + if (ctx->status & CHAN_OPENED) + dev_dbg(dev, "Opened "); + if (ctx->status & CHAN_ATTACHED) + dev_dbg(dev, "and attached"); + dev_dbg(dev, "\n"); + dev_dbg(dev, "-----------------------------------\n"); + dev_dbg(dev, "\n\n"); +} + +static void +xm2msc_pr_screg(struct device *dev, const void __iomem *base) +{ + dev_dbg(dev, "Ctr, GIE, IE, IS OUT\n"); + dev_dbg(dev, "0x%x 0x%x 0x%x 0x%x 0x%x\n", + xm2msc_readreg(base + XM2MSC_AP_CTRL), + xm2msc_readreg(base + XM2MSC_GIE), + xm2msc_readreg(base + XM2MSC_IER), + xm2msc_readreg(base + XM2MSC_ISR), + xm2msc_readreg(base + XM2MSC_NUM_OUTS)); +} + +static void +xm2msc_pr_chanreg(struct device *dev, struct xm2msc_chan_ctx *chan) +{ + const void __iomem *base = chan->regs; + + dev_dbg(dev, "WIN HIN INPIXELFMT INSTRIDE SRCB0L/H SRCB1L/H\n"); + dev_dbg(dev, "%d %d %d %d 0x%x/0x%x 0x%x/0x%x\n", + xm2msc_readreg(base + XM2MSC_WIDTHIN), + xm2msc_readreg(base + XM2MSC_HEIGHTIN), + xm2msc_readreg(base + XM2MSC_INPIXELFMT), + xm2msc_readreg(base + XM2MSC_INSTRIDE), + xm2msc_readreg(base + XM2MSC_SRCIMGBUF0), + xm2msc_readreg(base + XM2MSC_SRCIMGBUF0 + 4), + xm2msc_readreg(base + XM2MSC_SRCIMGBUF1), + xm2msc_readreg(base + XM2MSC_SRCIMGBUF1 + 4)); + dev_dbg(dev, "WOUT HOUT OUTPIXELFMT OUTSTRIDE DBUF0L/H DBUF1L/H\n"); + dev_dbg(dev, "%d %d %d %d 0x%x/0x%x 0x%x/0x%x\n", + xm2msc_readreg(base + XM2MSC_WIDTHOUT), + xm2msc_readreg(base + XM2MSC_HEIGHTOUT), + xm2msc_readreg(base + XM2MSC_OUTPIXELFMT), + xm2msc_readreg(base + XM2MSC_OUTSTRIDE), + xm2msc_readreg(base + XM2MSC_DSTIMGBUF0), + xm2msc_readreg(base + XM2MSC_DSTIMGBUF0 + 4), + chan->num == 4 ? + xm2msc_readreg(base + + XM2MSC_DSTIMGBUF1 + XM2MSC_RESERVED_AREA) : + xm2msc_readreg(base + XM2MSC_DSTIMGBUF1), + chan->num == 4 ? + xm2msc_readreg(base + + XM2MSC_DSTIMGBUF1 + XM2MSC_RESERVED_AREA + 4) : + xm2msc_readreg(base + XM2MSC_DSTIMGBUF1 + 4)); + + dev_dbg(dev, "LINERATE PIXELRATE\n"); + dev_dbg(dev, "0x%x 0x%x\n", + xm2msc_readreg(base + XM2MSC_LINERATE), + xm2msc_readreg(base + XM2MSC_PIXELRATE)); +} + +static void +xm2msc_pr_allchanreg(struct xm2m_msc_dev *xm2msc) +{ + unsigned int i; + struct xm2msc_chan_ctx *chan_ctx; + struct device *dev = xm2msc->dev; + + xm2msc_pr_screg(xm2msc->dev, xm2msc->regs); + + for (i = 0; i < xm2msc->running_chan; i++) { + chan_ctx = &xm2msc->xm2msc_chan[i]; + dev_dbg(dev, "Regs val for channel %d\n", i); + dev_dbg(dev, "______________________________________________\n"); + xm2msc_pr_chanreg(dev, chan_ctx); + dev_dbg(dev, "processed frames = %lu\n", chan_ctx->frames); + dev_dbg(dev, "______________________________________________\n"); + } +} + +static inline bool xm2msc_testbit(int num, u32 *addr) +{ + return (*addr & BIT(num)); +} + +static inline void xm2msc_setbit(int num, u32 *addr) +{ + *addr |= BIT(num); +} + +static inline void xm2msc_clrbit(int num, u32 *addr) +{ + *addr &= ~BIT(num); +} + +static void xm2msc_stop(struct xm2m_msc_dev *xm2msc) +{ + void __iomem *base = xm2msc->regs; + u32 data = xm2msc_readreg(base + XM2MSC_AP_CTRL); + + data &= ~XM2MSC_AP_CTRL_START; + xm2msc_writereg(base + XM2MSC_AP_CTRL, data); +} + +static void xm2msc_start(struct xm2m_msc_dev *xm2msc) +{ + void __iomem *base = xm2msc->regs; + u32 data = xm2msc_readreg(base + XM2MSC_AP_CTRL); + + data |= XM2MSC_AP_CTRL_START; + xm2msc_writereg(base + XM2MSC_AP_CTRL, data); +} + +static void xm2msc_set_chan(struct xm2msc_chan_ctx *ctx, bool state) +{ + mutex_lock(&ctx->xm2msc_dev->mutex); + if (state) + xm2msc_setbit(ctx->num, &ctx->xm2msc_dev->opened_chan); + else + xm2msc_clrbit(ctx->num, &ctx->xm2msc_dev->opened_chan); + mutex_unlock(&ctx->xm2msc_dev->mutex); +} + +static void +xm2msc_set_chan_stream(struct xm2msc_chan_ctx *ctx, bool state, int type) +{ + u32 *ptr; + + if (type == XM2MSC_CHAN_OUT) + ptr = &ctx->xm2msc_dev->out_streamed_chan; + else + ptr = &ctx->xm2msc_dev->cap_streamed_chan; + + spin_lock(&ctx->xm2msc_dev->lock); + if (state) + xm2msc_setbit(ctx->num, ptr); + else + xm2msc_clrbit(ctx->num, ptr); + + spin_unlock(&ctx->xm2msc_dev->lock); +} + +static int +xm2msc_chk_chan_stream(struct xm2msc_chan_ctx *ctx, int type) +{ + u32 *ptr; + int ret; + + if (type == XM2MSC_CHAN_OUT) + ptr = &ctx->xm2msc_dev->out_streamed_chan; + else + ptr = &ctx->xm2msc_dev->cap_streamed_chan; + + mutex_lock(&ctx->xm2msc_dev->mutex); + ret = xm2msc_testbit(ctx->num, ptr); + mutex_unlock(&ctx->xm2msc_dev->mutex); + + return ret; +} + +static void xm2msc_set_fmt(struct xm2m_msc_dev *xm2msc, u32 index) +{ + xm2msc_setbit(index, &xm2msc->supported_fmt); +} + +static int xm2msc_chk_fmt(struct xm2m_msc_dev *xm2msc, u32 index) +{ + return xm2msc_testbit(index, &xm2msc->supported_fmt); +} + +static void xm2msc_reset(struct xm2m_msc_dev *xm2msc) +{ + gpiod_set_value_cansleep(xm2msc->rst_gpio, XM2MSC_RESET_ASSERT); + gpiod_set_value_cansleep(xm2msc->rst_gpio, XM2MSC_RESET_DEASSERT); +} + +/* + * mem2mem callbacks + */ +static int xm2msc_job_ready(void *priv) +{ + struct xm2msc_chan_ctx *chan_ctx = priv; + + if ((v4l2_m2m_num_src_bufs_ready(chan_ctx->m2m_ctx) > 0) && + (v4l2_m2m_num_dst_bufs_ready(chan_ctx->m2m_ctx) > 0)) + return 1; + return 0; +} + +static bool xm2msc_alljob_ready(struct xm2m_msc_dev *xm2msc) +{ + struct xm2msc_chan_ctx *chan_ctx; + unsigned int chan; + + for (chan = 0; chan < xm2msc->running_chan; chan++) { + chan_ctx = &xm2msc->xm2msc_chan[chan]; + + if (!xm2msc_job_ready((void *)chan_ctx)) { + dev_dbg(xm2msc->dev, "chan %d not ready\n", + chan_ctx->num); + return false; + } + } + + return true; +} + +static void xm2msc_chan_abort_bufs(struct xm2msc_chan_ctx *chan_ctx) +{ + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + struct vb2_v4l2_buffer *dst_vb, *src_vb; + + spin_lock(&xm2msc->lock); + dev_dbg(xm2msc->dev, "aborting all buffers\n"); + + while (v4l2_m2m_num_src_bufs_ready(chan_ctx->m2m_ctx) > 0) { + src_vb = v4l2_m2m_src_buf_remove(chan_ctx->m2m_ctx); + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR); + } + + while (v4l2_m2m_num_dst_bufs_ready(chan_ctx->m2m_ctx) > 0) { + dst_vb = v4l2_m2m_dst_buf_remove(chan_ctx->m2m_ctx); + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR); + } + + v4l2_m2m_job_finish(chan_ctx->m2m_dev, chan_ctx->m2m_ctx); + spin_unlock(&xm2msc->lock); +} + +static void xm2msc_job_abort(void *priv) +{ + struct xm2msc_chan_ctx *chan_ctx = priv; + + xm2msc_chan_abort_bufs(chan_ctx); + + /* + * Stream off the channel as job_abort may not always + * be called after streamoff + */ + xm2msc_set_chan_stream(chan_ctx, false, XM2MSC_CHAN_OUT); + xm2msc_set_chan_stream(chan_ctx, false, XM2MSC_CHAN_CAP); +} + +static int xm2msc_set_bufaddr(struct xm2m_msc_dev *xm2msc) +{ + unsigned int chan; + u32 row_align; + struct xm2msc_chan_ctx *chan_ctx; + struct vb2_v4l2_buffer *src_vb, *dst_vb; + void __iomem *base; + struct xm2msc_q_data *q_data; + dma_addr_t src_luma, dst_luma; + dma_addr_t src_croma, dst_croma; + + if (!xm2msc_alljob_ready(xm2msc)) + return -EINVAL; + + for (chan = 0; chan < xm2msc->running_chan; chan++) { + chan_ctx = &xm2msc->xm2msc_chan[chan]; + base = chan_ctx->regs; + + src_vb = v4l2_m2m_next_src_buf(chan_ctx->m2m_ctx); + dst_vb = v4l2_m2m_next_dst_buf(chan_ctx->m2m_ctx); + + if (!src_vb || !dst_vb) { + v4l2_err(&xm2msc->v4l2_dev, "buffer not found chan = %d\n", + chan_ctx->num); + return -EINVAL; + } + + src_luma = vb2_dma_contig_plane_dma_addr(&src_vb->vb2_buf, 0); + dst_luma = vb2_dma_contig_plane_dma_addr(&dst_vb->vb2_buf, 0); + + q_data = &chan_ctx->q_data[XM2MSC_CHAN_OUT]; + row_align = chan_ctx->output_height_align; + if (chan_ctx->q_data[XM2MSC_CHAN_OUT].nbuffs == 2) + /* fmts having 2 planes 2 buffers */ + src_croma = + vb2_dma_contig_plane_dma_addr(&src_vb->vb2_buf, + 1); + else if (xm2msc_is_yuv_singlebuff(q_data->fmt->fourcc)) + /* fmts having 2 planes 1 contiguous buffer */ + src_croma = src_luma + + xm2msc_yuv_1stplane_size(q_data, row_align); + else /* fmts having 1 planes 1 contiguous buffer */ + src_croma = 0; + + q_data = &chan_ctx->q_data[XM2MSC_CHAN_CAP]; + row_align = chan_ctx->capture_height_align; + if (chan_ctx->q_data[XM2MSC_CHAN_CAP].nbuffs == 2) + dst_croma = + vb2_dma_contig_plane_dma_addr(&dst_vb->vb2_buf, + 1); + else if (xm2msc_is_yuv_singlebuff(q_data->fmt->fourcc)) + dst_croma = dst_luma + + xm2msc_yuv_1stplane_size(q_data, row_align); + else + dst_croma = 0; + + if (xm2msc->dma_addr_size == 64 && + sizeof(dma_addr_t) == sizeof(u64)) { + xm2msc_write64reg(base + XM2MSC_SRCIMGBUF0, src_luma); + xm2msc_write64reg(base + XM2MSC_SRCIMGBUF1, src_croma); + xm2msc_write64reg(base + XM2MSC_DSTIMGBUF0, dst_luma); + if (chan_ctx->num == 4) /* TODO: To be fixed in HW */ + xm2msc_write64reg(base + XM2MSC_DSTIMGBUF1 + + XM2MSC_RESERVED_AREA, + dst_croma); + else + xm2msc_write64reg(base + XM2MSC_DSTIMGBUF1, + dst_croma); + } else { + xm2msc_writereg(base + XM2MSC_SRCIMGBUF0, src_luma); + xm2msc_writereg(base + XM2MSC_SRCIMGBUF1, src_croma); + xm2msc_writereg(base + XM2MSC_DSTIMGBUF0, dst_luma); + if (chan_ctx->num == 4) /* TODO: To be fixed in HW */ + xm2msc_writereg(base + XM2MSC_DSTIMGBUF1 + + XM2MSC_RESERVED_AREA, + dst_croma); + else + xm2msc_writereg(base + XM2MSC_DSTIMGBUF1, + dst_croma); + } + } + return 0; +} + +static void xm2msc_job_finish(struct xm2m_msc_dev *xm2msc) +{ + unsigned int chan; + + for (chan = 0; chan < xm2msc->running_chan; chan++) { + struct xm2msc_chan_ctx *chan_ctx; + + chan_ctx = &xm2msc->xm2msc_chan[chan]; + v4l2_m2m_job_finish(chan_ctx->m2m_dev, chan_ctx->m2m_ctx); + } +} + +static void xm2msc_job_done(struct xm2m_msc_dev *xm2msc) +{ + u32 chan; + + for (chan = 0; chan < xm2msc->running_chan; chan++) { + struct xm2msc_chan_ctx *chan_ctx; + struct vb2_v4l2_buffer *src_vb, *dst_vb; + unsigned long flags; + + chan_ctx = &xm2msc->xm2msc_chan[chan]; + + src_vb = v4l2_m2m_src_buf_remove(chan_ctx->m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(chan_ctx->m2m_ctx); + + if (src_vb && dst_vb) { + dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; + dst_vb->timecode = src_vb->timecode; + dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + dst_vb->flags |= + src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; + + spin_lock_irqsave(&xm2msc->lock, flags); + v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); + spin_unlock_irqrestore(&xm2msc->lock, flags); + } + chan_ctx->frames++; + } +} + +static void xm2msc_device_run(void *priv) +{ + struct xm2msc_chan_ctx *chan_ctx = priv; + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + void __iomem *base = xm2msc->regs; + unsigned long flags; + int ret; + + spin_lock_irqsave(&xm2msc->lock, flags); + if (xm2msc->device_busy) { + spin_unlock_irqrestore(&xm2msc->lock, flags); + return; + } + xm2msc->device_busy = true; + + if (xm2msc->running_chan != NUM_STREAM(xm2msc)) { + dev_dbg(xm2msc->dev, "Running chan was %d\n", + xm2msc->running_chan); + xm2msc->running_chan = NUM_STREAM(xm2msc); + + /* IP need reset for updating of XM2MSC_NUM_OUT */ + xm2msc_reset(xm2msc); + xm2msc_writereg(base + XM2MSC_NUM_OUTS, xm2msc->running_chan); + xm2msc_program_allchan(xm2msc); + } + spin_unlock_irqrestore(&xm2msc->lock, flags); + + dev_dbg(xm2msc->dev, "Running chan = %d\n", xm2msc->running_chan); + if (!xm2msc->running_chan) { + xm2msc->device_busy = false; + return; + } + + ret = xm2msc_set_bufaddr(xm2msc); + if (ret) { + /* + * All channel does not have buffer + * Currently we do not handle the removal of any Intermediate + * channel while streaming is going on + */ + if (xm2msc->out_streamed_chan || xm2msc->cap_streamed_chan) + dev_err(xm2msc->dev, + "Buffer not available, streaming chan 0x%x\n", + xm2msc->cap_streamed_chan); + + xm2msc->device_busy = false; + return; + } + + xm2msc_writereg(base + XM2MSC_GIE, XM2MSC_GIE_EN); + xm2msc_writereg(base + XM2MSC_IER, XM2MSC_ISR_DONE); + + xm2msc_pr_status(xm2msc, __func__); + xm2msc_pr_screg(xm2msc->dev, base); + xm2msc_pr_allchanreg(xm2msc); + + xm2msc_start(xm2msc); + + xm2msc->isr_wait = true; + wait_event(xm2msc->isr_finished, !xm2msc->isr_wait); + + xm2msc_job_done(xm2msc); + + xm2msc->device_busy = false; + + if (xm2msc_alljob_ready(xm2msc)) + xm2msc_device_run(xm2msc->xm2msc_chan); + + xm2msc_job_finish(xm2msc); +} + +static irqreturn_t xm2msc_isr(int irq, void *data) +{ + struct xm2m_msc_dev *xm2msc = (struct xm2m_msc_dev *)data; + void __iomem *base = xm2msc->regs; + u32 status; + + status = xm2msc_readreg(base + XM2MSC_ISR); + if (!(status & XM2MSC_ISR_DONE)) + return IRQ_NONE; + + xm2msc_writereg(base + XM2MSC_ISR, status & XM2MSC_ISR_DONE); + + xm2msc_stop(xm2msc); + + xm2msc->isr_wait = false; + wake_up(&xm2msc->isr_finished); + + return IRQ_HANDLED; +} + +static int xm2msc_streamon(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + return v4l2_m2m_streamon(file, chan_ctx->m2m_ctx, type); +} + +static int xm2msc_streamoff(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + int ret; + + ret = v4l2_m2m_streamoff(file, chan_ctx->m2m_ctx, type); + + /* Check if any channel is still running */ + xm2msc_device_run(chan_ctx); + return ret; +} + +static int xm2msc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + return v4l2_m2m_qbuf(file, chan_ctx->m2m_ctx, buf); +} + +static int xm2msc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + return v4l2_m2m_dqbuf(file, chan_ctx->m2m_ctx, buf); +} + +static int xm2msc_expbuf(struct file *file, void *fh, + struct v4l2_exportbuffer *eb) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + return v4l2_m2m_expbuf(file, chan_ctx->m2m_ctx, eb); +} + +static int xm2msc_createbufs(struct file *file, void *fh, + struct v4l2_create_buffers *cb) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + return v4l2_m2m_create_bufs(file, chan_ctx->m2m_ctx, cb); +} + +static int xm2msc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *reqbufs) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + return v4l2_m2m_reqbufs(file, chan_ctx->m2m_ctx, reqbufs); +} + +static int xm2msc_querybuf(struct file *file, void *fh, + struct v4l2_buffer *buf) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + return v4l2_m2m_querybuf(file, chan_ctx->m2m_ctx, buf); +} + +static void +xm2msc_cal_imagesize(struct xm2msc_chan_ctx *chan_ctx, + struct xm2msc_q_data *q_data, u32 type) +{ + unsigned int i; + u32 fourcc = q_data->fmt->fourcc; + u32 height = q_data->height; + + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + height = ALIGN(height, chan_ctx->output_height_align); + else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + height = ALIGN(height, chan_ctx->capture_height_align); + + for (i = 0; i < q_data->nbuffs; i++) { + q_data->bytesperline[i] = q_data->stride; + q_data->sizeimage[i] = q_data->stride * height; + } + + switch (fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_XV15: + /* + * Adding chroma plane size as NV12/XV15 + * have a contiguous buffer for luma and chroma + */ + q_data->sizeimage[0] += + q_data->stride * (height / 2); + break; + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_XV15M: + q_data->sizeimage[1] = + q_data->stride * (height / 2); + break; + default: + break; + } +} + +static unsigned int +xm2msc_cal_stride(unsigned int width, enum xm2msc_pix_fmt xfmt, u8 ppc) +{ + unsigned int stride; + u32 align; + + /* Stride in Bytes = (Width × Bytes per Pixel); */ + switch (xfmt) { + case XILINX_M2MSC_FMT_RGBX8: + case XILINX_M2MSC_FMT_YUVX8: + case XILINX_M2MSC_FMT_RGBX10: + case XILINX_M2MSC_FMT_YUVX10: + case XILINX_M2MSC_FMT_BGRX8: + stride = width * 4; + break; + case XILINX_M2MSC_FMT_YUYV8: + case XILINX_M2MSC_FMT_UYVY8: + stride = width * 2; + break; + case XILINX_M2MSC_FMT_Y_UV8: + case XILINX_M2MSC_FMT_Y_UV8_420: + case XILINX_M2MSC_FMT_Y8: + stride = width * 1; + break; + case XILINX_M2MSC_FMT_RGB8: + case XILINX_M2MSC_FMT_YUV8: + case XILINX_M2MSC_FMT_BGR8: + stride = width * 3; + break; + case XILINX_M2MSC_FMT_Y_UV10: + case XILINX_M2MSC_FMT_Y_UV10_420: + case XILINX_M2MSC_FMT_Y10: + /* 4 bytes per 3 pixels */ + stride = DIV_ROUND_UP(width * 4, 3); + break; + default: + stride = 0; + } + + /* The data size is 64*pixels per clock bits */ + align = ppc * XM2MSC_ALIGN_MUL; + stride = ALIGN(stride, align); + + return stride; +} + +static int +vidioc_try_fmt(struct xm2msc_chan_ctx *chan_ctx, struct v4l2_format *f) +{ + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct xm2msc_q_data *q_data; + struct vb2_queue *vq; + int index; + + if (pix->width < XM2MSC_MIN_WIDTH || pix->width > xm2msc->max_wd || + pix->height < XM2MSC_MIN_HEIGHT || pix->height > xm2msc->max_ht) + dev_dbg(xm2msc->dev, + "Wrong input parameters %d, wxh: %dx%d.\n", + f->type, f->fmt.pix.width, f->fmt.pix.height); + + /* The width value must be a multiple of pixels per clock */ + if (pix->width % chan_ctx->xm2msc_dev->ppc) { + dev_info(xm2msc->dev, + "Wrong align parameters %d, wxh: %dx%d.\n", + f->type, f->fmt.pix.width, f->fmt.pix.height); + pix->width = ALIGN(pix->width, chan_ctx->xm2msc_dev->ppc); + } + + /* + * V4L2 specification suggests the driver corrects the + * format struct if any of the dimensions is unsupported + */ + if (pix->height < XM2MSC_MIN_HEIGHT) + pix->height = XM2MSC_MIN_HEIGHT; + else if (pix->height > xm2msc->max_ht) + pix->height = xm2msc->max_ht; + + if (pix->width < XM2MSC_MIN_WIDTH) + pix->width = XM2MSC_MIN_WIDTH; + else if (pix->width > xm2msc->max_wd) + pix->width = xm2msc->max_wd; + + vq = v4l2_m2m_get_vq(chan_ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(chan_ctx, f->type); + if (!q_data) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&xm2msc->v4l2_dev, + "%s queue busy\n", __func__); + return -EBUSY; + } + + q_data->fmt = find_format(f); + index = find_format_index(f); + if (!q_data->fmt || index == ARRAY_SIZE(formats) || + !xm2msc_chk_fmt(xm2msc, index)) { + v4l2_err(&xm2msc->v4l2_dev, + "Couldn't set format type %d, wxh: %dx%d. ", + f->type, f->fmt.pix.width, f->fmt.pix.height); + v4l2_err(&xm2msc->v4l2_dev, + "fmt: %d, field: %d\n", + f->fmt.pix.pixelformat, f->fmt.pix.field); + return -EINVAL; + } + + return 0; +} + +static void xm2msc_get_align(struct xm2msc_chan_ctx *chan_ctx) +{ + /* + * TODO: This is a temporary solution, will be reverted once stride and + * height align value come from application. + */ + chan_ctx->output_stride_align = output_stride_align[chan_ctx->num]; + chan_ctx->capture_stride_align = capture_stride_align[chan_ctx->num]; + chan_ctx->output_height_align = output_height_align[chan_ctx->num]; + chan_ctx->capture_height_align = capture_height_align[chan_ctx->num]; + if (output_stride_align[chan_ctx->num] != 1 || + capture_stride_align[chan_ctx->num] != 1 || + output_height_align[chan_ctx->num] != 1 || + capture_height_align[chan_ctx->num] != 1) { + dev_info(chan_ctx->xm2msc_dev->dev, + "You entered values other than default values.\n"); + dev_info(chan_ctx->xm2msc_dev->dev, + "Please note this may not be available for longer"); + dev_info(chan_ctx->xm2msc_dev->dev, + "and align values will come from application\n"); + dev_info(chan_ctx->xm2msc_dev->dev, + "value entered are -\n" + "output_stride_align = %d\n" + "output_height_align = %d\n" + "capture_stride_align = %d\n" + "capture_height_align = %d\n", + chan_ctx->output_stride_align, + chan_ctx->output_height_align, + chan_ctx->capture_stride_align, + chan_ctx->capture_height_align); + } +} + +static int +vidioc_s_fmt(struct xm2msc_chan_ctx *chan_ctx, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct xm2msc_q_data *q_data = get_q_data(chan_ctx, f->type); + unsigned int i; + unsigned int align = 1; + + q_data = get_q_data(chan_ctx, f->type); + + q_data->width = pix->width; + q_data->height = pix->height; + q_data->stride = xm2msc_cal_stride(pix->width, + q_data->fmt->xm2msc_fmt, + chan_ctx->xm2msc_dev->ppc); + + xm2msc_get_align(chan_ctx); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + align = chan_ctx->output_stride_align; + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + align = chan_ctx->capture_stride_align; + + q_data->stride = ALIGN(q_data->stride, align); + + q_data->colorspace = pix->colorspace; + q_data->field = pix->field; + q_data->nbuffs = q_data->fmt->num_buffs; + + xm2msc_cal_imagesize(chan_ctx, q_data, f->type); + + for (i = 0; i < q_data->nbuffs; i++) { + pix->plane_fmt[i].bytesperline = q_data->bytesperline[i]; + pix->plane_fmt[i].sizeimage = q_data->sizeimage[i]; + } + + xm2msc_pr_q(chan_ctx->xm2msc_dev->dev, q_data, + chan_ctx->num, f->type, __func__); + + return 0; +} + +static int xm2msc_try_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + return vidioc_try_fmt(chan_ctx, f); +} + +static int xm2msc_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + return vidioc_try_fmt(chan_ctx, f); +} + +static int xm2msc_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + int ret; + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + ret = xm2msc_try_fmt_vid_cap(file, fh, f); + if (ret) + return ret; + return vidioc_s_fmt(chan_ctx, f); +} + +static int xm2msc_s_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + int ret; + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + ret = xm2msc_try_fmt_vid_out(file, fh, f); + if (ret) + return ret; + + return vidioc_s_fmt(chan_ctx, f); +} + +static int vidioc_g_fmt(struct xm2msc_chan_ctx *chan_ctx, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct xm2msc_q_data *q_data; + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + unsigned int i; + + vq = v4l2_m2m_get_vq(chan_ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(chan_ctx, f->type); + if (!q_data) + return -EINVAL; + + pix->width = q_data->width; + pix->height = q_data->height; + pix->field = V4L2_FIELD_NONE; + pix->pixelformat = q_data->fmt->fourcc; + pix->colorspace = q_data->colorspace; + pix->num_planes = q_data->nbuffs; + + for (i = 0; i < pix->num_planes; i++) { + pix->plane_fmt[i].bytesperline = q_data->bytesperline[i]; + pix->plane_fmt[i].sizeimage = q_data->sizeimage[i]; + } + + return 0; +} + +static int xm2msc_g_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + return vidioc_g_fmt(chan_ctx, f); +} + +static int xm2msc_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + return vidioc_g_fmt(chan_ctx, f); +} + +static int enum_fmt(struct xm2m_msc_dev *xm2msc, struct v4l2_fmtdesc *f) +{ + const struct xm2msc_fmt *fmt; + unsigned int i, enabled = 0; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (xm2msc_chk_fmt(xm2msc, i) && enabled++ == f->index) + break; + } + + if (i == ARRAY_SIZE(formats)) + /* Format not found */ + return -EINVAL; + + /* Format found */ + fmt = &formats[i]; + strlcpy(f->description, fmt->name, + sizeof(f->description)); + f->pixelformat = fmt->fourcc; + + return 0; +} + +static int xm2msc_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + return enum_fmt(chan_ctx->xm2msc_dev, f); +} + +static int xm2msc_enum_fmt_vid_out(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(fh); + + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + return enum_fmt(chan_ctx->xm2msc_dev, f); +} + +static int xm2msc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strncpy(cap->driver, XM2MSC_DRIVER_NAME, sizeof(cap->driver) - 1); + strncpy(cap->card, XM2MSC_DRIVER_NAME, sizeof(cap->card) - 1); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", XM2MSC_DRIVER_NAME); + /* + * This is only a mem-to-mem video device. The STREAMING + * device capability flags are left only for compatibility + * and are scheduled for removal. + */ + cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int xm2msc_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + unsigned int i; + struct xm2msc_chan_ctx *chan_ctx = vb2_get_drv_priv(vq); + struct xm2msc_q_data *q_data; + + q_data = get_q_data(chan_ctx, vq->type); + if (!q_data) + return -EINVAL; + + *nplanes = q_data->nbuffs; + + for (i = 0; i < *nplanes; i++) + sizes[i] = q_data->sizeimage[i]; + + dev_dbg(chan_ctx->xm2msc_dev->dev, "get %d buffer(s) of size %d", + *nbuffers, sizes[0]); + if (q_data->nbuffs == 2) + dev_dbg(chan_ctx->xm2msc_dev->dev, " and %d\n", sizes[1]); + + return 0; +} + +static int xm2msc_buf_prepare(struct vb2_buffer *vb) +{ + struct xm2msc_chan_ctx *chan_ctx = vb2_get_drv_priv(vb->vb2_queue); + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + struct xm2msc_q_data *q_data; + unsigned int i, num_buffs; + + q_data = get_q_data(chan_ctx, vb->vb2_queue->type); + if (!q_data) + return -EINVAL; + num_buffs = q_data->nbuffs; + + for (i = 0; i < num_buffs; i++) { + if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { + v4l2_err(&xm2msc->v4l2_dev, "data will not fit into plane "); + v4l2_err(&xm2msc->v4l2_dev, "(%lu < %lu)\n", + vb2_plane_size(vb, i), + (long)q_data->sizeimage[i]); + return -EINVAL; + } + } + + for (i = 0; i < num_buffs; i++) + vb2_set_plane_payload(vb, i, q_data->sizeimage[i]); + + return 0; +} + +static void xm2msc_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct xm2msc_chan_ctx *chan_ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(chan_ctx->m2m_ctx, vbuf); +} + +static void xm2msc_return_all_buffers(struct xm2msc_chan_ctx *chan_ctx, + struct vb2_queue *q, + enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *vb; + unsigned long flags; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(q->type)) + vb = v4l2_m2m_src_buf_remove(chan_ctx->m2m_ctx); + else + vb = v4l2_m2m_dst_buf_remove(chan_ctx->m2m_ctx); + if (!vb) + break; + spin_lock_irqsave(&chan_ctx->xm2msc_dev->lock, flags); + v4l2_m2m_buf_done(vb, state); + spin_unlock_irqrestore(&chan_ctx->xm2msc_dev->lock, flags); + } +} + +static int xm2msc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct xm2msc_chan_ctx *chan_ctx = vb2_get_drv_priv(q); + static struct xm2msc_q_data *q_data; + int type; + + if (V4L2_TYPE_IS_OUTPUT(q->type)) + xm2msc_set_chan_stream(chan_ctx, true, XM2MSC_CHAN_OUT); + else + xm2msc_set_chan_stream(chan_ctx, true, XM2MSC_CHAN_CAP); + + xm2msc_set_chan_params(chan_ctx, q->type); + + if (xm2msc_chk_chan_stream(chan_ctx, XM2MSC_CHAN_CAP) && + xm2msc_chk_chan_stream(chan_ctx, XM2MSC_CHAN_OUT)) + xm2msc_set_chan_com_params(chan_ctx); + + type = V4L2_TYPE_IS_OUTPUT(q->type) ? + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q_data = get_q_data(chan_ctx, type); + xm2msc_pr_q(chan_ctx->xm2msc_dev->dev, q_data, chan_ctx->num, + type, __func__); + xm2msc_pr_status(chan_ctx->xm2msc_dev, __func__); + + return 0; +} + +static void xm2msc_stop_streaming(struct vb2_queue *q) +{ + struct xm2msc_chan_ctx *chan_ctx = vb2_get_drv_priv(q); + + xm2msc_return_all_buffers(chan_ctx, q, VB2_BUF_STATE_ERROR); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) + xm2msc_set_chan_stream(chan_ctx, false, XM2MSC_CHAN_OUT); + else + xm2msc_set_chan_stream(chan_ctx, false, XM2MSC_CHAN_CAP); +} + +static const struct vb2_ops xm2msc_qops = { + .queue_setup = xm2msc_queue_setup, + .buf_prepare = xm2msc_buf_prepare, + .buf_queue = xm2msc_buf_queue, + .start_streaming = xm2msc_start_streaming, + .stop_streaming = xm2msc_stop_streaming, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct xm2msc_chan_ctx *chan_ctx = priv; + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = chan_ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &xm2msc_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &xm2msc->dev_mutex; + src_vq->dev = xm2msc->v4l2_dev.dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; + dst_vq->drv_priv = chan_ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &xm2msc_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &xm2msc->dev_mutex; + dst_vq->dev = xm2msc->v4l2_dev.dev; + + return vb2_queue_init(dst_vq); +} + +static const struct v4l2_ioctl_ops xm2msc_ioctl_ops = { + .vidioc_querycap = xm2msc_querycap, + + .vidioc_enum_fmt_vid_cap_mplane = xm2msc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane = xm2msc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap_mplane = xm2msc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap_mplane = xm2msc_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out_mplane = xm2msc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out_mplane = xm2msc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out_mplane = xm2msc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out_mplane = xm2msc_s_fmt_vid_out, + + .vidioc_reqbufs = xm2msc_reqbufs, + .vidioc_querybuf = xm2msc_querybuf, + .vidioc_expbuf = xm2msc_expbuf, + .vidioc_create_bufs = xm2msc_createbufs, + + .vidioc_qbuf = xm2msc_qbuf, + .vidioc_dqbuf = xm2msc_dqbuf, + + .vidioc_streamon = xm2msc_streamon, + .vidioc_streamoff = xm2msc_streamoff, +}; + +static void xm2msc_set_q_data(struct xm2msc_chan_ctx *chan_ctx, + const struct xm2msc_fmt *fmt, + enum v4l2_buf_type type) +{ + struct xm2msc_q_data *q_data; + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + + q_data = get_q_data(chan_ctx, type); + + q_data->fmt = fmt; + q_data->width = xm2msc->max_wd; + q_data->height = xm2msc->max_ht; + q_data->field = V4L2_FIELD_NONE; + q_data->nbuffs = q_data->fmt->num_buffs; + + q_data->stride = xm2msc_cal_stride(q_data->width, + q_data->fmt->xm2msc_fmt, + xm2msc->ppc); + + xm2msc_cal_imagesize(chan_ctx, q_data, type); +} + +static int xm2msc_set_chan_parm(struct xm2msc_chan_ctx *chan_ctx) +{ + int ret = 0; + unsigned int i; + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + + chan_ctx->output_stride_align = 1; + chan_ctx->output_height_align = 1; + chan_ctx->capture_stride_align = 1; + chan_ctx->capture_height_align = 1; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (xm2msc_chk_fmt(xm2msc, i)) + break; + } + + /* No supported format */ + if (i == ARRAY_SIZE(formats)) { + dev_err(xm2msc->dev, "no supported format found\n"); + return -EINVAL; + } + + xm2msc_set_q_data(chan_ctx, &formats[i], + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + xm2msc_set_q_data(chan_ctx, &formats[i], + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + return ret; +} + +static int xm2msc_open(struct file *file) +{ + struct xm2m_msc_dev *xm2msc = video_drvdata(file); + struct xm2msc_chan_ctx *chan_ctx = NULL; + u32 minor, chan; + int ret; + + if (mutex_lock_interruptible(&xm2msc->dev_mutex)) + return -ERESTARTSYS; + + minor = iminor(file_inode(file)); + + for (chan = 0; chan < xm2msc->max_chan; chan++) { + chan_ctx = &xm2msc->xm2msc_chan[chan]; + + if ((chan_ctx->status & CHAN_ATTACHED) && + chan_ctx->minor == minor) + break; + } + + if (chan == xm2msc->max_chan) { + v4l2_err(&xm2msc->v4l2_dev, + "%s Chan not found with minor = %d\n", + __func__, minor); + ret = -EBADF; + goto unlock; + } + + /* Already opened, do not allow same channel + * to be open more then once + */ + if (chan_ctx->status & CHAN_OPENED) { + v4l2_warn(&xm2msc->v4l2_dev, + "%s Chan already opened for minor = %d\n", + __func__, minor); + ret = -EBUSY; + goto unlock; + } + + v4l2_fh_init(&chan_ctx->fh, &chan_ctx->vfd); + file->private_data = &chan_ctx->fh; + v4l2_fh_add(&chan_ctx->fh); + + chan_ctx->m2m_ctx = v4l2_m2m_ctx_init(chan_ctx->m2m_dev, + chan_ctx, &queue_init); + if (IS_ERR(chan_ctx->m2m_ctx)) { + ret = PTR_ERR(chan_ctx->m2m_ctx); + v4l2_err(&xm2msc->v4l2_dev, + "%s Chan M2M CTX not creted for minor %d\n", + __func__, minor); + goto error_m2m; + } + + chan_ctx->fh.m2m_ctx = chan_ctx->m2m_ctx; + chan_ctx->status |= CHAN_OPENED; + chan_ctx->xm2msc_dev = xm2msc; + chan_ctx->frames = 0; + + xm2msc_set_chan(chan_ctx, true); + + v4l2_info(&xm2msc->v4l2_dev, "Channel %d instance created\n", chan); + + mutex_unlock(&xm2msc->dev_mutex); + xm2msc_pr_chanctx(chan_ctx, __func__); + xm2msc_pr_status(xm2msc, __func__); + return 0; + +error_m2m: + v4l2_fh_del(&chan_ctx->fh); + v4l2_fh_exit(&chan_ctx->fh); +unlock: + mutex_unlock(&xm2msc->dev_mutex); + xm2msc_pr_chanctx(chan_ctx, __func__); + xm2msc_pr_status(xm2msc, __func__); + return ret; +} + +static int xm2msc_release(struct file *file) +{ + struct xm2m_msc_dev *xm2msc = video_drvdata(file); + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(file->private_data); + + if (mutex_lock_interruptible(&xm2msc->dev_mutex)) + return -ERESTARTSYS; + + v4l2_m2m_ctx_release(chan_ctx->m2m_ctx); + v4l2_fh_del(&chan_ctx->fh); + v4l2_fh_exit(&chan_ctx->fh); + chan_ctx->status &= ~CHAN_OPENED; + xm2msc_set_chan(chan_ctx, false); + + v4l2_info(&xm2msc->v4l2_dev, "Channel %d instance released\n", + chan_ctx->num); + + mutex_unlock(&xm2msc->dev_mutex); + return 0; +} + +static unsigned int xm2msc_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct xm2msc_chan_ctx *chan_ctx = fh_to_chanctx(file->private_data); + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + int ret; + + mutex_lock(&xm2msc->dev_mutex); + ret = v4l2_m2m_poll(file, chan_ctx->m2m_ctx, wait); + mutex_unlock(&xm2msc->dev_mutex); + + return ret; +} + +static int xm2msc_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct xm2msc_chan_ctx *chan_ctx = file->private_data; + struct xm2m_msc_dev *xm2msc = chan_ctx->xm2msc_dev; + int ret; + + mutex_lock(&xm2msc->dev_mutex); + ret = v4l2_m2m_mmap(file, chan_ctx->m2m_ctx, vma); + + mutex_unlock(&xm2msc->dev_mutex); + return ret; +} + +static const struct v4l2_file_operations xm2msc_fops = { + .owner = THIS_MODULE, + .open = xm2msc_open, + .release = xm2msc_release, + .poll = xm2msc_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = xm2msc_mmap, +}; + +static const struct video_device xm2msc_videodev = { + .name = XM2MSC_DRIVER_NAME, + .fops = &xm2msc_fops, + .ioctl_ops = &xm2msc_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .vfl_dir = VFL_DIR_M2M, +}; + +static const struct v4l2_m2m_ops xm2msc_m2m_ops = { + .device_run = xm2msc_device_run, + .job_ready = xm2msc_job_ready, + .job_abort = xm2msc_job_abort, +}; + +static int xm2msc_parse_of(struct platform_device *pdev, + struct xm2m_msc_dev *xm2msc) +{ + struct resource *res; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + int hw_vid_fmt_cnt; + const char *vid_fmts[ARRAY_SIZE(formats)]; + int ret; + u32 i, j; + + xm2msc->clk = devm_clk_get(dev, NULL); + if (IS_ERR(xm2msc->clk)) { + ret = PTR_ERR(xm2msc->clk); + dev_err(dev, "failed to get clk (%d)\n", ret); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xm2msc->regs = devm_ioremap_resource(dev, res); + if (IS_ERR((__force void *)xm2msc->regs)) + return PTR_ERR((__force const void *)xm2msc->regs); + + dev_dbg(dev, "IO Mem 0x%llx mapped at %p\n", res->start, xm2msc->regs); + + ret = of_property_read_u32(node, "xlnx,max-chan", + &xm2msc->max_chan); + if (ret < 0) + return ret; + + if (xm2msc->max_chan < XM2MSC_MIN_CHAN || + xm2msc->max_chan > XM2MSC_MAX_CHAN) { + dev_err(dev, + "Invalid maximum scaler channels : %d", + xm2msc->max_chan); + return -EINVAL; + } + + ret = of_property_read_u32(node, "xlnx,max-width", + &xm2msc->max_wd); + if (ret < 0) { + dev_err(dev, + "missing xlnx,max-width prop\n"); + return ret; + } + + if (xm2msc->max_wd < XM2MSC_MIN_WIDTH || + xm2msc->max_wd > XM2MSC_MAX_WIDTH) { + dev_err(dev, "Invalid width : %d", + xm2msc->max_wd); + return -EINVAL; + } + + ret = of_property_read_u32(node, "xlnx,max-height", + &xm2msc->max_ht); + if (ret < 0) { + dev_err(dev, "missing xlnx,max-height prop\n"); + return ret; + } + + if (xm2msc->max_ht < XM2MSC_MIN_HEIGHT || + xm2msc->max_ht > XM2MSC_MAX_HEIGHT) { + dev_err(dev, "Invalid height : %d", + xm2msc->max_ht); + return -EINVAL; + } + + ret = of_property_read_u32(node, "xlnx,dma-addr-width", + &xm2msc->dma_addr_size); + if (ret || (xm2msc->dma_addr_size != 32 && + xm2msc->dma_addr_size != 64)) { + dev_err(dev, "missing/invalid addr width dts prop\n"); + return -EINVAL; + } + + ret = of_property_read_u8(node, "xlnx,pixels-per-clock", + &xm2msc->ppc); + if (ret || (xm2msc->ppc != 1 && xm2msc->ppc != 2 && xm2msc->ppc != 4)) { + dev_err(dev, "missing or invalid pixels per clock dts prop\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "xlnx,num-taps", + &xm2msc->taps); + if (ret || (xm2msc->taps != XSCALER_TAPS_6 && + xm2msc->taps != XSCALER_TAPS_8 && + xm2msc->taps != XSCALER_TAPS_10 && + xm2msc->taps != XSCALER_TAPS_12)) { + dev_err(dev, "missing/invalid taps in dts prop\n"); + return -EINVAL; + } + + xm2msc->irq = irq_of_parse_and_map(node, 0); + if (xm2msc->irq < 0) { + dev_err(dev, "Unable to get IRQ"); + return xm2msc->irq; + } + + dev_dbg(dev, "Max Channel Supported = %d\n", xm2msc->max_chan); + dev_dbg(dev, "DMA Addr width Supported = %d\n", xm2msc->dma_addr_size); + dev_dbg(dev, "Max col/row Supported = (%d) / (%d)\n", + xm2msc->max_wd, xm2msc->max_ht); + dev_dbg(dev, "taps Supported = %d\n", xm2msc->taps); + /* read supported video formats and update internal table */ + hw_vid_fmt_cnt = of_property_count_strings(node, "xlnx,vid-formats"); + + ret = of_property_read_string_array(node, "xlnx,vid-formats", + vid_fmts, hw_vid_fmt_cnt); + if (ret < 0) { + dev_err(dev, + "Missing or invalid xlnx,vid-formats dts prop\n"); + return ret; + } + + dev_dbg(dev, "Supported format = "); + for (i = 0; i < hw_vid_fmt_cnt; i++) { + const char *vid_fmt_name = vid_fmts[i]; + + for (j = 0; j < ARRAY_SIZE(formats); j++) { + const char *dts_name = formats[j].name; + + if (strcmp(vid_fmt_name, dts_name)) + continue; + dev_dbg(dev, "%s ", dts_name); + + xm2msc_set_fmt(xm2msc, j); + } + } + dev_dbg(dev, "\n"); + xm2msc->rst_gpio = devm_gpiod_get(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(xm2msc->rst_gpio)) { + ret = PTR_ERR(xm2msc->rst_gpio); + if (ret == -EPROBE_DEFER) + dev_info(dev, + "Probe deferred due to GPIO reset defer\n"); + else + dev_err(dev, + "Unable to locate reset property in dt\n"); + return ret; + } + + return 0; +} + +static void xm2msc_unreg_video_n_m2m(struct xm2m_msc_dev *xm2msc) +{ + struct xm2msc_chan_ctx *chan_ctx; + unsigned int chan; + + for (chan = 0; chan < xm2msc->max_chan; chan++) { + chan_ctx = &xm2msc->xm2msc_chan[chan]; + if (!(chan_ctx->status & CHAN_ATTACHED)) + break; /*We register video sequentially */ + video_unregister_device(&chan_ctx->vfd); + chan_ctx->status &= ~CHAN_ATTACHED; + + if (!IS_ERR(chan_ctx->m2m_dev)) + v4l2_m2m_release(chan_ctx->m2m_dev); + } +} + +static int xm2m_msc_probe(struct platform_device *pdev) +{ + int ret; + struct xm2m_msc_dev *xm2msc; + struct xm2msc_chan_ctx *chan_ctx; + struct video_device *vfd; + unsigned int chan; + + xm2msc = devm_kzalloc(&pdev->dev, sizeof(*xm2msc), GFP_KERNEL); + if (!xm2msc) + return -ENOMEM; + + ret = xm2msc_parse_of(pdev, xm2msc); + if (ret < 0) + return ret; + + xm2msc->dev = &pdev->dev; + + ret = clk_prepare_enable(xm2msc->clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clk (%d)\n", ret); + return ret; + } + + xm2msc_reset(xm2msc); + + spin_lock_init(&xm2msc->lock); + + ret = v4l2_device_register(&pdev->dev, &xm2msc->v4l2_dev); + if (ret) + goto reg_dev_err; + + for (chan = 0; chan < xm2msc->max_chan; chan++) { + chan_ctx = &xm2msc->xm2msc_chan[chan]; + + vfd = &chan_ctx->vfd; + *vfd = xm2msc_videodev; + vfd->lock = &xm2msc->dev_mutex; + vfd->v4l2_dev = &xm2msc->v4l2_dev; + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, chan); + if (ret) { + v4l2_err(&xm2msc->v4l2_dev, + "Failed to register video dev for chan %d\n", + chan); + goto unreg_dev; + } + + chan_ctx->status = CHAN_ATTACHED; + + video_set_drvdata(vfd, xm2msc); + snprintf(vfd->name, sizeof(vfd->name), + "%s", xm2msc_videodev.name); + v4l2_info(&xm2msc->v4l2_dev, + " Device registered as /dev/video%d\n", vfd->num); + + dev_dbg(xm2msc->dev, "%s Device registered as /dev/video%d\n", + __func__, vfd->num); + + chan_ctx->m2m_dev = v4l2_m2m_init(&xm2msc_m2m_ops); + if (IS_ERR(chan_ctx->m2m_dev)) { + v4l2_err(&xm2msc->v4l2_dev, + "Failed to init mem2mem device for chan %d\n", + chan); + ret = PTR_ERR(chan_ctx->m2m_dev); + goto unreg_dev; + } + chan_ctx->xm2msc_dev = xm2msc; + chan_ctx->regs = xm2msc->regs + XM2MSC_CHAN_REGS_START(chan); + if (chan > 4) /* TODO: To be fixed in HW */ + chan_ctx->regs += XM2MSC_RESERVED_AREA; + chan_ctx->num = chan; + chan_ctx->minor = vfd->minor; + + /* Set channel parameters to default values */ + ret = xm2msc_set_chan_parm(chan_ctx); + if (ret) + goto unreg_dev; + + xm2msc_pr_chanctx(chan_ctx, __func__); + } + + mutex_init(&xm2msc->dev_mutex); + mutex_init(&xm2msc->mutex); + init_waitqueue_head(&xm2msc->isr_finished); + + ret = devm_request_irq(&pdev->dev, xm2msc->irq, + xm2msc_isr, IRQF_SHARED, + XM2MSC_DRIVER_NAME, xm2msc); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to register IRQ\n"); + goto unreg_dev; + } + + platform_set_drvdata(pdev, xm2msc); + + return 0; + +unreg_dev: + xm2msc_unreg_video_n_m2m(xm2msc); + v4l2_device_unregister(&xm2msc->v4l2_dev); +reg_dev_err: + clk_disable_unprepare(xm2msc->clk); + return ret; +} + +static int xm2m_msc_remove(struct platform_device *pdev) +{ + struct xm2m_msc_dev *xm2msc = platform_get_drvdata(pdev); + + xm2msc_unreg_video_n_m2m(xm2msc); + v4l2_device_unregister(&xm2msc->v4l2_dev); + clk_disable_unprepare(xm2msc->clk); + return 0; +} + +static const struct of_device_id xm2m_msc_of_id_table[] = { + {.compatible = "xlnx,v-multi-scaler-v1.0"}, + {} +}; + +MODULE_DEVICE_TABLE(of, xm2m_msc_of_id_table); + +static struct platform_driver xm2m_msc_driver = { + .driver = { + .name = "xilinx-multiscaler", + .of_match_table = xm2m_msc_of_id_table, + }, + .probe = xm2m_msc_probe, + .remove = xm2m_msc_remove, +}; + +module_platform_driver(xm2m_msc_driver); + +MODULE_DESCRIPTION("Xilinx M2M Multi-Scaler Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("xlnx_m2m_multiscaler_dev"); diff --git a/drivers/media/platform/xilinx/xilinx-remapper.c b/drivers/media/platform/xilinx/xilinx-remapper.c new file mode 100644 index 000000000000..d2e84ec1f2d6 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-remapper.c @@ -0,0 +1,546 @@ +/* + * Xilinx Video Remapper + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-vip.h" + +#define XREMAP_MIN_WIDTH 1 +#define XREMAP_DEF_WIDTH 1920 +#define XREMAP_MAX_WIDTH 65535 +#define XREMAP_MIN_HEIGHT 1 +#define XREMAP_DEF_HEIGHT 1080 +#define XREMAP_MAX_HEIGHT 65535 + +#define XREMAP_PAD_SINK 0 +#define XREMAP_PAD_SOURCE 1 + +/** + * struct xremap_mapping_output - Output format description + * @code: media bus pixel core after remapping + * @num_components: number of pixel components after remapping + * @component_maps: configuration array corresponding to this output + */ +struct xremap_mapping_output { + u32 code; + unsigned int num_components; + unsigned int component_maps[4]; +}; + +/** + * struct xremap_mapping - Input-output remapping description + * @code: media bus pixel code before remapping + * @width: video bus width in bits + * @num_components: number of pixel components before remapping + * @outputs: array of possible output formats + */ +struct xremap_mapping { + u32 code; + unsigned int width; + unsigned int num_components; + const struct xremap_mapping_output *outputs; +}; + +/** + * struct xremap_device - Xilinx Test Pattern Generator device structure + * @xvip: Xilinx Video IP device + * @pads: media pads + * @formats: V4L2 media bus formats at the sink and source pads + * @config: device configuration parsed from its DT node + * @config.width: video bus width in bits + * @config.num_s_components: number of pixel components at the input + * @config.num_m_components: number of pixel components at the output + * @config.component_maps: component remapping configuration + * @default_mapping: Default mapping compatible with the configuration + * @default_output: Default output format for the default mapping + */ +struct xremap_device { + struct xvip_device xvip; + struct media_pad pads[2]; + struct v4l2_mbus_framefmt formats[2]; + + struct { + unsigned int width; + unsigned int num_s_components; + unsigned int num_m_components; + unsigned int component_maps[4]; + } config; + + const struct xremap_mapping *default_mapping; + const struct xremap_mapping_output *default_output; +}; + +static inline struct xremap_device *to_remap(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xremap_device, xvip.subdev); +} + +/* ----------------------------------------------------------------------------- + * Mappings + */ + +static const struct xremap_mapping xremap_mappings[] = { + { + .code = MEDIA_BUS_FMT_RBG888_1X24, + .width = 8, + .num_components = 3, + .outputs = (const struct xremap_mapping_output[]) { + { MEDIA_BUS_FMT_RGB888_1X32_PADHI, 4, { 1, 0, 2, 4 } }, + { }, + }, + }, +}; + +static const struct xremap_mapping_output * +xremap_match_mapping(struct xremap_device *xremap, + const struct xremap_mapping *mapping) +{ + const struct xremap_mapping_output *output; + + if (mapping->width != xremap->config.width || + mapping->num_components != xremap->config.num_s_components) + return NULL; + + for (output = mapping->outputs; output->code; ++output) { + unsigned int i; + + if (output->num_components != xremap->config.num_m_components) + continue; + + for (i = 0; i < output->num_components; ++i) { + if (output->component_maps[i] != + xremap->config.component_maps[i]) + break; + } + + if (i == output->num_components) + return output; + } + + return NULL; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int xremap_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct xremap_device *xremap = to_remap(subdev); + struct v4l2_mbus_framefmt *format; + + if (code->pad == XREMAP_PAD_SINK) { + const struct xremap_mapping *mapping = NULL; + unsigned int index = code->index + 1; + unsigned int i; + + /* Iterate through the mappings and skip the ones that don't + * match the remapper configuration until we reach the requested + * index. + */ + for (i = 0; i < ARRAY_SIZE(xremap_mappings) && index; ++i) { + mapping = &xremap_mappings[i]; + + if (xremap_match_mapping(xremap, mapping)) + index--; + } + + /* If the index was larger than the number of supported mappings + * return -EINVAL. + */ + if (index > 0) + return -EINVAL; + + code->code = mapping->code; + } else { + if (code->index) + return -EINVAL; + + format = v4l2_subdev_get_try_format(subdev, cfg, code->pad); + code->code = format->code; + } + + return 0; +} + +static int xremap_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == XREMAP_PAD_SINK) { + /* The remapper doesn't restrict the size on the sink pad. */ + fse->min_width = XREMAP_MIN_WIDTH; + fse->max_width = XREMAP_MAX_WIDTH; + fse->min_height = XREMAP_MIN_HEIGHT; + fse->max_height = XREMAP_MAX_HEIGHT; + } else { + /* The size on the source pad are fixed and always identical to + * the size on the sink pad. + */ + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} + +static struct v4l2_mbus_framefmt * +xremap_get_pad_format(struct xremap_device *xremap, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xremap->xvip.subdev, cfg, + pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xremap->formats[pad]; + default: + return NULL; + } +} + +static int xremap_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xremap_device *xremap = to_remap(subdev); + + fmt->format = *xremap_get_pad_format(xremap, cfg, fmt->pad, fmt->which); + + return 0; +} + +static int xremap_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xremap_device *xremap = to_remap(subdev); + const struct xremap_mapping_output *output; + const struct xremap_mapping *mapping; + struct v4l2_mbus_framefmt *format; + unsigned int i; + + format = xremap_get_pad_format(xremap, cfg, fmt->pad, fmt->which); + + if (fmt->pad == XREMAP_PAD_SOURCE) { + fmt->format = *format; + return 0; + } + + /* Find the mapping. If the requested format has no mapping, use the + * default. + */ + for (i = 0; i < ARRAY_SIZE(xremap_mappings); ++i) { + mapping = &xremap_mappings[i]; + if (mapping->code != fmt->format.code) + continue; + + output = xremap_match_mapping(xremap, mapping); + if (output) + break; + } + + if (!output) { + mapping = xremap->default_mapping; + output = xremap->default_output; + } + + format->code = mapping->code; + format->width = clamp_t(unsigned int, fmt->format.width, + XREMAP_MIN_WIDTH, XREMAP_MAX_WIDTH); + format->height = clamp_t(unsigned int, fmt->format.height, + XREMAP_MIN_HEIGHT, XREMAP_MAX_HEIGHT); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = xremap_get_pad_format(xremap, cfg, XREMAP_PAD_SOURCE, + fmt->which); + *format = fmt->format; + format->code = output->code; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +/* + * xremap_init_formats - Initialize formats on all pads + * @subdev: remapper V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static void xremap_init_formats(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + struct xremap_device *xremap = to_remap(subdev); + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + + format.pad = XREMAP_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = xremap->default_mapping->code; + format.format.width = XREMAP_DEF_WIDTH; + format.format.height = XREMAP_DEF_HEIGHT; + + xremap_set_format(subdev, fh ? fh->pad : NULL, &format); +} + +static int xremap_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + xremap_init_formats(subdev, fh); + + return 0; +} + +static int xremap_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static struct v4l2_subdev_core_ops xremap_core_ops = { +}; + +static struct v4l2_subdev_video_ops xremap_video_ops = { +}; + +static struct v4l2_subdev_pad_ops xremap_pad_ops = { + .enum_mbus_code = xremap_enum_mbus_code, + .enum_frame_size = xremap_enum_frame_size, + .get_fmt = xremap_get_format, + .set_fmt = xremap_set_format, +}; + +static struct v4l2_subdev_ops xremap_ops = { + .core = &xremap_core_ops, + .video = &xremap_video_ops, + .pad = &xremap_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xremap_internal_ops = { + .open = xremap_open, + .close = xremap_close, +}; + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static const struct media_entity_operations xremap_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static int xremap_parse_of(struct xremap_device *xremap) +{ + struct device_node *node = xremap->xvip.dev->of_node; + unsigned int i; + int ret; + + /* Parse the DT properties. */ + ret = of_property_read_u32(node, "xlnx,video-width", + &xremap->config.width); + if (ret < 0) { + dev_dbg(xremap->xvip.dev, "unable to parse %s property\n", + "xlnx,video-width"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "#xlnx,s-components", + &xremap->config.num_s_components); + if (ret < 0) { + dev_dbg(xremap->xvip.dev, "unable to parse %s property\n", + "#xlnx,s-components"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "#xlnx,m-components", + &xremap->config.num_m_components); + if (ret < 0) { + dev_dbg(xremap->xvip.dev, "unable to parse %s property\n", + "#xlnx,m-components"); + return -EINVAL; + } + + ret = of_property_read_u32_array(node, "xlnx,component-maps", + xremap->config.component_maps, + xremap->config.num_m_components); + if (ret < 0) { + dev_dbg(xremap->xvip.dev, "unable to parse %s property\n", + "xlnx,component-maps"); + return -EINVAL; + } + + /* Validate the parsed values. */ + if (xremap->config.num_s_components > 4 || + xremap->config.num_m_components > 4) { + dev_dbg(xremap->xvip.dev, + "invalid number of components (s %u m %u)\n", + xremap->config.num_s_components, + xremap->config.num_m_components); + return -EINVAL; + } + + for (i = 0; i < xremap->config.num_m_components; ++i) { + if (xremap->config.component_maps[i] > 4) { + dev_dbg(xremap->xvip.dev, "invalid map %u @%u\n", + xremap->config.component_maps[i], i); + return -EINVAL; + } + } + + /* Find the first mapping that matches the remapper configuration and + * store it as the default mapping. + */ + for (i = 0; i < ARRAY_SIZE(xremap_mappings); ++i) { + const struct xremap_mapping_output *output; + const struct xremap_mapping *mapping; + + mapping = &xremap_mappings[i]; + output = xremap_match_mapping(xremap, mapping); + + if (output) { + xremap->default_mapping = mapping; + xremap->default_output = output; + return 0; + } + } + + dev_err(xremap->xvip.dev, + "No format compatible with device configuration\n"); + + return -EINVAL; +} + +static int xremap_probe(struct platform_device *pdev) +{ + struct xremap_device *xremap; + struct v4l2_subdev *subdev; + int ret; + + xremap = devm_kzalloc(&pdev->dev, sizeof(*xremap), GFP_KERNEL); + if (!xremap) + return -ENOMEM; + + xremap->xvip.dev = &pdev->dev; + + ret = xremap_parse_of(xremap); + if (ret < 0) + return ret; + + xremap->xvip.clk = devm_clk_get(xremap->xvip.dev, NULL); + if (IS_ERR(xremap->xvip.clk)) + return PTR_ERR(xremap->xvip.clk); + + clk_prepare_enable(xremap->xvip.clk); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xremap->xvip.subdev; + v4l2_subdev_init(subdev, &xremap_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xremap_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xremap); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + xremap_init_formats(subdev, NULL); + + xremap->pads[XREMAP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xremap->pads[XREMAP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &xremap_media_ops; + ret = media_entity_pads_init(&subdev->entity, 2, xremap->pads); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, xremap); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + dev_info(&pdev->dev, "device registered\n"); + + return 0; + +error: + media_entity_cleanup(&subdev->entity); + clk_disable_unprepare(xremap->xvip.clk); + return ret; +} + +static int xremap_remove(struct platform_device *pdev) +{ + struct xremap_device *xremap = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xremap->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + + clk_disable_unprepare(xremap->xvip.clk); + + return 0; +} + +static const struct of_device_id xremap_of_id_table[] = { + { .compatible = "xlnx,v-remapper" }, + { } +}; +MODULE_DEVICE_TABLE(of, xremap_of_id_table); + +static struct platform_driver xremap_driver = { + .driver = { + .name = "xilinx-remapper", + .of_match_table = xremap_of_id_table, + }, + .probe = xremap_probe, + .remove = xremap_remove, +}; + +module_platform_driver(xremap_driver); + +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_DESCRIPTION("Xilinx Video Remapper Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-rgb2yuv.c b/drivers/media/platform/xilinx/xilinx-rgb2yuv.c new file mode 100644 index 000000000000..20ae95946ca3 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-rgb2yuv.c @@ -0,0 +1,566 @@ +/* + * Xilinx RGB to YUV Convertor + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/xilinx-v4l2-controls.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-vip.h" + +#define XRGB2YUV_YMAX 0x100 +#define XRGB2YUV_YMIN 0x104 +#define XRGB2YUV_CBMAX 0x108 +#define XRGB2YUV_CBMIN 0x10c +#define XRGB2YUV_CRMAX 0x110 +#define XRGB2YUV_CRMIN 0x114 +#define XRGB2YUV_YOFFSET 0x118 +#define XRGB2YUV_CBOFFSET 0x11c +#define XRGB2YUV_CROFFSET 0x120 +#define XRGB2YUV_ACOEF 0x124 +#define XRGB2YUV_BCOEF 0x128 +#define XRGB2YUV_CCOEF 0x12c +#define XRGB2YUV_DCOEF 0x130 + +/** + * struct xrgb2yuv_device - Xilinx RGB2YUV device structure + * @xvip: Xilinx Video IP device + * @pads: media pads + * @formats: V4L2 media bus formats at the sink and source pads + * @default_formats: default V4L2 media bus formats + * @vip_formats: Xilinx Video IP formats + * @ctrl_handler: control handler + */ +struct xrgb2yuv_device { + struct xvip_device xvip; + + struct media_pad pads[2]; + + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct xvip_video_format *vip_formats[2]; + + struct v4l2_ctrl_handler ctrl_handler; +}; + +static inline struct xrgb2yuv_device *to_rgb2yuv(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xrgb2yuv_device, xvip.subdev); +} + +/* + * V4L2 Subdevice Video Operations + */ + +static int xrgb2yuv_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev); + + if (!enable) { + xvip_stop(&xrgb2yuv->xvip); + return 0; + } + + xvip_set_frame_size(&xrgb2yuv->xvip, &xrgb2yuv->formats[XVIP_PAD_SINK]); + + xvip_start(&xrgb2yuv->xvip); + + return 0; +} + +/* + * V4L2 Subdevice Pad Operations + */ + +static struct v4l2_mbus_framefmt * +__xrgb2yuv_get_pad_format(struct xrgb2yuv_device *xrgb2yuv, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xrgb2yuv->xvip.subdev, cfg, + pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xrgb2yuv->formats[pad]; + default: + return NULL; + } +} + +static int xrgb2yuv_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev); + + fmt->format = *__xrgb2yuv_get_pad_format(xrgb2yuv, cfg, fmt->pad, + fmt->which); + + return 0; +} + +static int xrgb2yuv_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev); + struct v4l2_mbus_framefmt *format; + + format = __xrgb2yuv_get_pad_format(xrgb2yuv, cfg, fmt->pad, fmt->which); + + if (fmt->pad == XVIP_PAD_SOURCE) { + fmt->format = *format; + return 0; + } + + xvip_set_format_size(format, fmt); + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = __xrgb2yuv_get_pad_format(xrgb2yuv, cfg, XVIP_PAD_SOURCE, + fmt->which); + + xvip_set_format_size(format, fmt); + + return 0; +} + +/* + * V4L2 Subdevice Operations + */ + +static int xrgb2yuv_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev); + struct v4l2_mbus_framefmt *format; + + /* Initialize with default formats */ + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK); + *format = xrgb2yuv->default_formats[XVIP_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE); + *format = xrgb2yuv->default_formats[XVIP_PAD_SOURCE]; + + return 0; +} + +static int xrgb2yuv_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static int xrgb2yuv_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct xrgb2yuv_device *xrgb2yuv = + container_of(ctrl->handler, struct xrgb2yuv_device, + ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_XILINX_RGB2YUV_YMAX: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_YMAX, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_YMIN: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_YMIN, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_CBMAX: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CBMAX, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_CBMIN: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CBMIN, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_CRMAX: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CRMAX, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_CRMIN: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CRMIN, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_YOFFSET: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_YOFFSET, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_CBOFFSET: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CBOFFSET, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_CROFFSET: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CROFFSET, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_ACOEF: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_ACOEF, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_BCOEF: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_BCOEF, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_CCOEF: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CCOEF, ctrl->val); + return 0; + case V4L2_CID_XILINX_RGB2YUV_DCOEF: + xvip_write(&xrgb2yuv->xvip, XRGB2YUV_DCOEF, ctrl->val); + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops xrgb2yuv_ctrl_ops = { + .s_ctrl = xrgb2yuv_s_ctrl, +}; + +static struct v4l2_subdev_video_ops xrgb2yuv_video_ops = { + .s_stream = xrgb2yuv_s_stream, +}; + +static struct v4l2_subdev_pad_ops xrgb2yuv_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xvip_enum_frame_size, + .get_fmt = xrgb2yuv_get_format, + .set_fmt = xrgb2yuv_set_format, +}; + +static struct v4l2_subdev_ops xrgb2yuv_ops = { + .video = &xrgb2yuv_video_ops, + .pad = &xrgb2yuv_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xrgb2yuv_internal_ops = { + .open = xrgb2yuv_open, + .close = xrgb2yuv_close, +}; + +/* + * Control Configs + */ + +static struct v4l2_ctrl_config xrgb2yuv_ctrls[] = { + { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_YMAX, + .name = "RGB to YUV: Maximum Y value", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_YMIN, + .name = "RGB to YUV: Minimum Y value", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_CBMAX, + .name = "RGB to YUV: Maximum Cb value", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_CBMIN, + .name = "RGB to YUV: Minimum Cb value", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_CRMAX, + .name = "RGB to YUV: Maximum Cr value", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_CRMIN, + .name = "RGB to YUV: Minimum Cr value", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 16) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_YOFFSET, + .name = "RGB to YUV: Luma offset", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 17) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_CBOFFSET, + .name = "RGB to YUV: Chroma Cb offset", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 17) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_CROFFSET, + .name = "RGB to YUV: Chroma Cr offset", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 17) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_ACOEF, + .name = "RGB to YUV: CA coefficient", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = -((1 << 17) - 1), + .max = (1 << 17) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_BCOEF, + .name = "RGB to YUV: CB coefficient", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = -((1 << 17) - 1), + .max = (1 << 17) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_CCOEF, + .name = "RGB to YUV: CC coefficient", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = -((1 << 17) - 1), + .max = (1 << 17) - 1, + .step = 1, + }, { + .ops = &xrgb2yuv_ctrl_ops, + .id = V4L2_CID_XILINX_RGB2YUV_DCOEF, + .name = "RGB to YUV: CD coefficient", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = -((1 << 17) - 1), + .max = (1 << 17) - 1, + .step = 1, + }, +}; + +/* + * Media Operations + */ + +static const struct media_entity_operations xrgb2yuv_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * Power Management + */ + +static int __maybe_unused xrgb2yuv_pm_suspend(struct device *dev) +{ + struct xrgb2yuv_device *xrgb2yuv = dev_get_drvdata(dev); + + xvip_suspend(&xrgb2yuv->xvip); + + return 0; +} + +static int __maybe_unused xrgb2yuv_pm_resume(struct device *dev) +{ + struct xrgb2yuv_device *xrgb2yuv = dev_get_drvdata(dev); + + xvip_resume(&xrgb2yuv->xvip); + + return 0; +} + +/* + * Platform Device Driver + */ + +static int xrgb2yuv_parse_of(struct xrgb2yuv_device *xrgb2yuv) +{ + struct device *dev = xrgb2yuv->xvip.dev; + struct device_node *node = xrgb2yuv->xvip.dev->of_node; + struct device_node *ports; + struct device_node *port; + u32 port_id; + int ret; + + ports = of_get_child_by_name(node, "ports"); + if (ports == NULL) + ports = node; + + /* Get the format description for each pad */ + for_each_child_of_node(ports, port) { + if (port->name && (of_node_cmp(port->name, "port") == 0)) { + const struct xvip_video_format *vip_format; + + vip_format = xvip_of_get_format(port); + if (IS_ERR(vip_format)) { + dev_err(dev, "invalid format in DT"); + return PTR_ERR(vip_format); + } + + ret = of_property_read_u32(port, "reg", &port_id); + if (ret < 0) { + dev_err(dev, "no reg in DT"); + return ret; + } + + if (port_id != 0 && port_id != 1) { + dev_err(dev, "invalid reg in DT"); + return -EINVAL; + } + + xrgb2yuv->vip_formats[port_id] = vip_format; + } + } + + return 0; +} + +static int xrgb2yuv_probe(struct platform_device *pdev) +{ + struct xrgb2yuv_device *xrgb2yuv; + struct v4l2_subdev *subdev; + struct v4l2_mbus_framefmt *default_format; + unsigned int i; + int ret; + + xrgb2yuv = devm_kzalloc(&pdev->dev, sizeof(*xrgb2yuv), GFP_KERNEL); + if (!xrgb2yuv) + return -ENOMEM; + + xrgb2yuv->xvip.dev = &pdev->dev; + + ret = xrgb2yuv_parse_of(xrgb2yuv); + if (ret < 0) + return ret; + + ret = xvip_init_resources(&xrgb2yuv->xvip); + if (ret < 0) + return ret; + + /* Reset and initialize the core */ + xvip_reset(&xrgb2yuv->xvip); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xrgb2yuv->xvip.subdev; + v4l2_subdev_init(subdev, &xrgb2yuv_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xrgb2yuv_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xrgb2yuv); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Initialize default and active formats */ + default_format = &xrgb2yuv->default_formats[XVIP_PAD_SINK]; + default_format->code = xrgb2yuv->vip_formats[XVIP_PAD_SINK]->code; + default_format->field = V4L2_FIELD_NONE; + default_format->colorspace = V4L2_COLORSPACE_SRGB; + xvip_get_frame_size(&xrgb2yuv->xvip, default_format); + + xrgb2yuv->formats[XVIP_PAD_SINK] = *default_format; + + default_format = &xrgb2yuv->default_formats[XVIP_PAD_SOURCE]; + *default_format = xrgb2yuv->default_formats[XVIP_PAD_SINK]; + default_format->code = xrgb2yuv->vip_formats[XVIP_PAD_SOURCE]->code; + + xrgb2yuv->formats[XVIP_PAD_SOURCE] = *default_format; + + xrgb2yuv->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xrgb2yuv->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &xrgb2yuv_media_ops; + ret = media_entity_pads_init(&subdev->entity, 2, xrgb2yuv->pads); + if (ret < 0) + goto error; + + v4l2_ctrl_handler_init(&xrgb2yuv->ctrl_handler, 13); + + for (i = 0; i < ARRAY_SIZE(xrgb2yuv_ctrls); i++) { + xrgb2yuv_ctrls[i].def = xvip_read(&xrgb2yuv->xvip, + XRGB2YUV_YMAX + i * 4); + v4l2_ctrl_new_custom(&xrgb2yuv->ctrl_handler, + &xrgb2yuv_ctrls[i], NULL); + } + + if (xrgb2yuv->ctrl_handler.error) { + dev_err(&pdev->dev, "failed to add controls\n"); + ret = xrgb2yuv->ctrl_handler.error; + goto error; + } + subdev->ctrl_handler = &xrgb2yuv->ctrl_handler; + + platform_set_drvdata(pdev, xrgb2yuv); + + xvip_print_version(&xrgb2yuv->xvip); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + return 0; + +error: + v4l2_ctrl_handler_free(&xrgb2yuv->ctrl_handler); + media_entity_cleanup(&subdev->entity); + xvip_cleanup_resources(&xrgb2yuv->xvip); + return ret; +} + +static int xrgb2yuv_remove(struct platform_device *pdev) +{ + struct xrgb2yuv_device *xrgb2yuv = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xrgb2yuv->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&xrgb2yuv->ctrl_handler); + media_entity_cleanup(&subdev->entity); + + xvip_cleanup_resources(&xrgb2yuv->xvip); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xrgb2yuv_pm_ops, xrgb2yuv_pm_suspend, + xrgb2yuv_pm_resume); + +static const struct of_device_id xrgb2yuv_of_id_table[] = { + { .compatible = "xlnx,v-rgb2yuv-7.1" }, + { } +}; +MODULE_DEVICE_TABLE(of, xrgb2yuv_of_id_table); + +static struct platform_driver xrgb2yuv_driver = { + .driver = { + .name = "xilinx-rgb2yuv", + .pm = &xrgb2yuv_pm_ops, + .of_match_table = xrgb2yuv_of_id_table, + }, + .probe = xrgb2yuv_probe, + .remove = xrgb2yuv_remove, +}; + +module_platform_driver(xrgb2yuv_driver); + +MODULE_DESCRIPTION("Xilinx RGB to YUV Converter Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-scaler.c b/drivers/media/platform/xilinx/xilinx-scaler.c new file mode 100644 index 000000000000..bb0d52627a50 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-scaler.c @@ -0,0 +1,708 @@ +/* + * Xilinx Scaler + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/fixp-arith.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-vip.h" + +#define XSCALER_MIN_WIDTH 32 +#define XSCALER_MAX_WIDTH 4096 +#define XSCALER_MIN_HEIGHT 32 +#define XSCALER_MAX_HEIGHT 4096 + +#define XSCALER_HSF 0x0100 +#define XSCALER_VSF 0x0104 +#define XSCALER_SF_SHIFT 20 +#define XSCALER_SF_MASK 0xffffff +#define XSCALER_SOURCE_SIZE 0x0108 +#define XSCALER_SIZE_HORZ_SHIFT 0 +#define XSCALER_SIZE_VERT_SHIFT 16 +#define XSCALER_SIZE_MASK 0xfff +#define XSCALER_HAPERTURE 0x010c +#define XSCALER_VAPERTURE 0x0110 +#define XSCALER_APERTURE_START_SHIFT 0 +#define XSCALER_APERTURE_END_SHIFT 16 +#define XSCALER_OUTPUT_SIZE 0x0114 +#define XSCALER_COEF_DATA_IN 0x0134 +#define XSCALER_COEF_DATA_IN_SHIFT 16 + +/* Fixed point operations */ +#define FRAC_N 8 + +static inline s16 fixp_new(s16 a) +{ + return a << FRAC_N; +} + +static inline s16 fixp_mult(s16 a, s16 b) +{ + return ((s32)(a * b)) >> FRAC_N; +} + +/** + * struct xscaler_device - Xilinx Scaler device structure + * @xvip: Xilinx Video IP device + * @pads: media pads + * @formats: V4L2 media bus formats at the sink and source pads + * @default_formats: default V4L2 media bus formats + * @vip_format: Xilinx Video IP format + * @crop: Active crop rectangle for the sink pad + * @num_hori_taps: number of vertical taps + * @num_vert_taps: number of vertical taps + * @max_num_phases: maximum number of phases + * @separate_yc_coef: separate coefficients for Luma(y) and Chroma(c) + * @separate_hv_coef: separate coefficients for Horizontal(h) and Vertical(v) + */ +struct xscaler_device { + struct xvip_device xvip; + + struct media_pad pads[2]; + + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct xvip_video_format *vip_format; + struct v4l2_rect crop; + + u32 num_hori_taps; + u32 num_vert_taps; + u32 max_num_phases; + bool separate_yc_coef; + bool separate_hv_coef; +}; + +static inline struct xscaler_device *to_scaler(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xscaler_device, xvip.subdev); +} + +/* + * V4L2 Subdevice Video Operations + */ + +/** + * lanczos - Lanczos 2D FIR kernel convolution + * @x: phase + * @a: Lanczos kernel size + * + * Return: the coefficient value in fixed point format. + */ +static s16 lanczos(s16 x, s16 a) +{ + s16 pi; + s16 numerator; + s16 denominator; + s16 temp; + + if (x < -a || x > a) + return 0; + else if (x == 0) + return fixp_new(1); + + /* a * sin(pi * x) * sin(pi * x / a) / (pi * pi * x * x) */ + + pi = (fixp_new(157) << FRAC_N) / fixp_new(50); + + if (x < 0) + x = -x; + + /* sin(pi * x) */ + temp = fixp_mult(fixp_new(180), x); + temp = fixp_sin16(temp >> FRAC_N); + + /* a * sin(pi * x) */ + numerator = fixp_mult(temp, a); + + /* sin(pi * x / a) */ + temp = (fixp_mult(fixp_new(180), x) << FRAC_N) / a; + temp = fixp_sin16(temp >> FRAC_N); + + /* a * sin(pi * x) * sin(pi * x / a) */ + numerator = fixp_mult(temp, numerator); + + /* pi * pi * x * x */ + denominator = fixp_mult(pi, pi); + temp = fixp_mult(x, x); + denominator = fixp_mult(temp, denominator); + + return (numerator << FRAC_N) / denominator; +} + +/** + * xscaler_set_coefs - generate and program the coefficient table + * @xscaler: scaler device + * @taps: maximum coefficient tap index + * + * Generate the coefficient table using Lanczos resampling, and program + * generated coefficients to the scaler. The generated coefficients are + * supposed to work regardless of resolutions. + * + * Return: 0 if the coefficient table is programmed, and -ENOMEM if memory + * allocation for the table fails. + */ +static int xscaler_set_coefs(struct xscaler_device *xscaler, s16 taps) +{ + s16 *coef; + s16 dy; + u32 coef_val; + u16 phases = xscaler->max_num_phases; + u16 i; + u16 j; + + coef = kcalloc(phases, sizeof(*coef), GFP_KERNEL); + if (!coef) + return -ENOMEM; + + for (i = 0; i < phases; i++) { + s16 sum = 0; + + dy = ((fixp_new(i) << FRAC_N) / fixp_new(phases)); + + /* Generate Lanczos coefficients */ + for (j = 0; j < taps; j++) { + coef[j] = lanczos(fixp_new(j - (taps >> 1)) + dy, + fixp_new(taps >> 1)); + sum += coef[j]; + } + + /* Program coefficients */ + for (j = 0; j < taps; j += 2) { + /* Normalize and multiply coefficients */ + coef_val = (((coef[j] << FRAC_N) << (FRAC_N - 2)) / + sum) & 0xffff; + if (j + 1 < taps) + coef_val |= ((((coef[j + 1] << FRAC_N) << + (FRAC_N - 2)) / sum) & 0xffff) << + 16; + + xvip_write(&xscaler->xvip, XSCALER_COEF_DATA_IN, + coef_val); + } + } + + kfree(coef); + + return 0; +} + +static void xscaler_set_aperture(struct xscaler_device *xscaler) +{ + u16 start; + u16 end; + u32 scale_factor; + + xvip_disable_reg_update(&xscaler->xvip); + + /* set horizontal aperture */ + start = xscaler->crop.left; + end = start + xscaler->crop.width - 1; + xvip_write(&xscaler->xvip, XSCALER_HAPERTURE, + (end << XSCALER_APERTURE_END_SHIFT) | + (start << XSCALER_APERTURE_START_SHIFT)); + + /* set vertical aperture */ + start = xscaler->crop.top; + end = start + xscaler->crop.height - 1; + xvip_write(&xscaler->xvip, XSCALER_VAPERTURE, + (end << XSCALER_APERTURE_END_SHIFT) | + (start << XSCALER_APERTURE_START_SHIFT)); + + /* set scaling factors */ + scale_factor = ((xscaler->crop.width << XSCALER_SF_SHIFT) / + xscaler->formats[XVIP_PAD_SOURCE].width) & + XSCALER_SF_MASK; + xvip_write(&xscaler->xvip, XSCALER_HSF, scale_factor); + + scale_factor = ((xscaler->crop.height << XSCALER_SF_SHIFT) / + xscaler->formats[XVIP_PAD_SOURCE].height) & + XSCALER_SF_MASK; + xvip_write(&xscaler->xvip, XSCALER_VSF, scale_factor); + + xvip_enable_reg_update(&xscaler->xvip); +} + +static int xscaler_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xscaler_device *xscaler = to_scaler(subdev); + u32 width; + u32 height; + + if (!enable) { + xvip_stop(&xscaler->xvip); + return 0; + } + + /* set input width / height */ + width = xscaler->formats[XVIP_PAD_SINK].width; + height = xscaler->formats[XVIP_PAD_SINK].height; + xvip_write(&xscaler->xvip, XSCALER_SOURCE_SIZE, + (height << XSCALER_SIZE_VERT_SHIFT) | + (width << XSCALER_SIZE_HORZ_SHIFT)); + + /* set output width / height */ + width = xscaler->formats[XVIP_PAD_SOURCE].width; + height = xscaler->formats[XVIP_PAD_SOURCE].height; + xvip_write(&xscaler->xvip, XSCALER_OUTPUT_SIZE, + (height << XSCALER_SIZE_VERT_SHIFT) | + (width << XSCALER_SIZE_HORZ_SHIFT)); + + /* set aperture */ + xscaler_set_aperture(xscaler); + + xvip_start(&xscaler->xvip); + + return 0; +} + +/* + * V4L2 Subdevice Pad Operations + */ + +static int xscaler_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + fse->min_width = XSCALER_MIN_WIDTH; + fse->max_width = XSCALER_MAX_WIDTH; + fse->min_height = XSCALER_MIN_HEIGHT; + fse->max_height = XSCALER_MAX_HEIGHT; + + return 0; +} + +static struct v4l2_mbus_framefmt * +__xscaler_get_pad_format(struct xscaler_device *xscaler, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xscaler->xvip.subdev, cfg, + pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xscaler->formats[pad]; + default: + return NULL; + } +} + +static struct v4l2_rect *__xscaler_get_crop(struct xscaler_device *xscaler, + struct v4l2_subdev_pad_config *cfg, + u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&xscaler->xvip.subdev, cfg, + XVIP_PAD_SINK); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xscaler->crop; + default: + return NULL; + } +} + +static int xscaler_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xscaler_device *xscaler = to_scaler(subdev); + + fmt->format = *__xscaler_get_pad_format(xscaler, cfg, fmt->pad, + fmt->which); + + return 0; +} + +static void xscaler_try_crop(const struct v4l2_mbus_framefmt *sink, + struct v4l2_rect *crop) +{ + + crop->left = min_t(u32, crop->left, sink->width - XSCALER_MIN_WIDTH); + crop->top = min_t(u32, crop->top, sink->height - XSCALER_MIN_HEIGHT); + crop->width = clamp_t(u32, crop->width, XSCALER_MIN_WIDTH, + sink->width - crop->left); + crop->height = clamp_t(u32, crop->height, XSCALER_MIN_HEIGHT, + sink->height - crop->top); +} + +static int xscaler_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xscaler_device *xscaler = to_scaler(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + format = __xscaler_get_pad_format(xscaler, cfg, fmt->pad, fmt->which); + + format->width = clamp_t(unsigned int, fmt->format.width, + XSCALER_MIN_WIDTH, XSCALER_MAX_WIDTH); + format->height = clamp_t(unsigned int, fmt->format.height, + XSCALER_MIN_HEIGHT, XSCALER_MAX_HEIGHT); + + fmt->format = *format; + + if (fmt->pad == XVIP_PAD_SINK) { + /* Set the crop rectangle to the full frame */ + crop = __xscaler_get_crop(xscaler, cfg, fmt->which); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + } + + return 0; +} + +static int xscaler_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct xscaler_device *xscaler = to_scaler(subdev); + struct v4l2_mbus_framefmt *format; + + if (sel->pad != XVIP_PAD_SINK) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + format = __xscaler_get_pad_format(xscaler, cfg, XVIP_PAD_SINK, + sel->which); + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format->width; + sel->r.height = format->height; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = *__xscaler_get_crop(xscaler, cfg, sel->which); + return 0; + default: + return -EINVAL; + } +} + +static int xscaler_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct xscaler_device *xscaler = to_scaler(subdev); + struct v4l2_mbus_framefmt *format; + + if ((sel->target != V4L2_SEL_TGT_CROP) || (sel->pad != XVIP_PAD_SINK)) + return -EINVAL; + + format = __xscaler_get_pad_format(xscaler, cfg, XVIP_PAD_SINK, + sel->which); + xscaler_try_crop(format, &sel->r); + *__xscaler_get_crop(xscaler, cfg, sel->which) = sel->r; + + return 0; +} + +/* + * V4L2 Subdevice Operations + */ + +static int xscaler_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct xscaler_device *xscaler = to_scaler(subdev); + struct v4l2_mbus_framefmt *format; + + /* Initialize with default formats */ + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK); + *format = xscaler->default_formats[XVIP_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE); + *format = xscaler->default_formats[XVIP_PAD_SOURCE]; + + return 0; +} + +static int xscaler_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static struct v4l2_subdev_video_ops xscaler_video_ops = { + .s_stream = xscaler_s_stream, +}; + +static struct v4l2_subdev_pad_ops xscaler_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xscaler_enum_frame_size, + .get_fmt = xscaler_get_format, + .set_fmt = xscaler_set_format, + .get_selection = xscaler_get_selection, + .set_selection = xscaler_set_selection, +}; + +static struct v4l2_subdev_ops xscaler_ops = { + .video = &xscaler_video_ops, + .pad = &xscaler_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xscaler_internal_ops = { + .open = xscaler_open, + .close = xscaler_close, +}; + +/* + * Media Operations + */ + +static const struct media_entity_operations xscaler_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * Power Management + */ + +static int __maybe_unused xscaler_pm_suspend(struct device *dev) +{ + struct xscaler_device *xscaler = dev_get_drvdata(dev); + + xvip_suspend(&xscaler->xvip); + + return 0; +} + +static int __maybe_unused xscaler_pm_resume(struct device *dev) +{ + struct xscaler_device *xscaler = dev_get_drvdata(dev); + + xvip_resume(&xscaler->xvip); + + return 0; +} + +/* + * Platform Device Driver + */ + +static int xscaler_parse_of(struct xscaler_device *xscaler) +{ + struct device *dev = xscaler->xvip.dev; + struct device_node *node = xscaler->xvip.dev->of_node; + struct device_node *ports; + struct device_node *port; + int ret; + + ports = of_get_child_by_name(node, "ports"); + if (ports == NULL) + ports = node; + + /* Get the format description for each pad */ + for_each_child_of_node(ports, port) { + if (port->name && (of_node_cmp(port->name, "port") == 0)) { + const struct xvip_video_format *vip_format; + + vip_format = xvip_of_get_format(port); + if (IS_ERR(vip_format)) { + dev_err(dev, "invalid format in DT"); + return PTR_ERR(vip_format); + } + + if (!xscaler->vip_format) { + xscaler->vip_format = vip_format; + } else if (xscaler->vip_format != vip_format) { + dev_err(dev, "in/out format mismatch in DT"); + return -EINVAL; + } + } + } + + ret = of_property_read_u32(node, "xlnx,num-hori-taps", + &xscaler->num_hori_taps); + if (ret < 0) + return ret; + + ret = of_property_read_u32(node, "xlnx,num-vert-taps", + &xscaler->num_vert_taps); + if (ret < 0) + return ret; + + ret = of_property_read_u32(node, "xlnx,max-num-phases", + &xscaler->max_num_phases); + if (ret < 0) + return ret; + + xscaler->separate_yc_coef = + of_property_read_bool(node, "xlnx,separate-yc-coef"); + + xscaler->separate_hv_coef = + of_property_read_bool(node, "xlnx,separate-hv-coef"); + + return 0; +} + +static int xscaler_probe(struct platform_device *pdev) +{ + struct xscaler_device *xscaler; + struct v4l2_subdev *subdev; + struct v4l2_mbus_framefmt *default_format; + u32 size; + int ret; + + xscaler = devm_kzalloc(&pdev->dev, sizeof(*xscaler), GFP_KERNEL); + if (!xscaler) + return -ENOMEM; + + xscaler->xvip.dev = &pdev->dev; + + ret = xscaler_parse_of(xscaler); + if (ret < 0) + return ret; + + ret = xvip_init_resources(&xscaler->xvip); + if (ret < 0) + return ret; + + /* Reset and initialize the core */ + xvip_reset(&xscaler->xvip); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xscaler->xvip.subdev; + v4l2_subdev_init(subdev, &xscaler_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xscaler_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xscaler); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Initialize default and active formats */ + default_format = &xscaler->default_formats[XVIP_PAD_SINK]; + default_format->code = xscaler->vip_format->code; + default_format->field = V4L2_FIELD_NONE; + default_format->colorspace = V4L2_COLORSPACE_SRGB; + size = xvip_read(&xscaler->xvip, XSCALER_SOURCE_SIZE); + default_format->width = (size >> XSCALER_SIZE_HORZ_SHIFT) & + XSCALER_SIZE_MASK; + default_format->height = (size >> XSCALER_SIZE_VERT_SHIFT) & + XSCALER_SIZE_MASK; + + xscaler->formats[XVIP_PAD_SINK] = *default_format; + + default_format = &xscaler->default_formats[XVIP_PAD_SOURCE]; + *default_format = xscaler->default_formats[XVIP_PAD_SINK]; + size = xvip_read(&xscaler->xvip, XSCALER_OUTPUT_SIZE); + default_format->width = (size >> XSCALER_SIZE_HORZ_SHIFT) & + XSCALER_SIZE_MASK; + default_format->height = (size >> XSCALER_SIZE_VERT_SHIFT) & + XSCALER_SIZE_MASK; + + xscaler->formats[XVIP_PAD_SOURCE] = *default_format; + + xscaler->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xscaler->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &xscaler_media_ops; + + ret = media_entity_pads_init(&subdev->entity, 2, xscaler->pads); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, xscaler); + + xvip_print_version(&xscaler->xvip); + + ret = xscaler_set_coefs(xscaler, (s16)xscaler->num_hori_taps); + if (ret < 0) + goto error; + + if (xscaler->separate_hv_coef) { + ret = xscaler_set_coefs(xscaler, (s16)xscaler->num_vert_taps); + if (ret < 0) + goto error; + } + + if (xscaler->separate_yc_coef) { + ret = xscaler_set_coefs(xscaler, (s16)xscaler->num_hori_taps); + if (ret < 0) + goto error; + + if (xscaler->separate_hv_coef) { + ret = xscaler_set_coefs(xscaler, + (s16)xscaler->num_vert_taps); + if (ret < 0) + goto error; + } + } + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + return 0; + +error: + media_entity_cleanup(&subdev->entity); + xvip_cleanup_resources(&xscaler->xvip); + return ret; +} + +static int xscaler_remove(struct platform_device *pdev) +{ + struct xscaler_device *xscaler = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xscaler->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + + xvip_cleanup_resources(&xscaler->xvip); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xscaler_pm_ops, xscaler_pm_suspend, xscaler_pm_resume); + +static const struct of_device_id xscaler_of_id_table[] = { + { .compatible = "xlnx,v-scaler-8.1" }, + { } +}; +MODULE_DEVICE_TABLE(of, xscaler_of_id_table); + +static struct platform_driver xscaler_driver = { + .driver = { + .name = "xilinx-scaler", + .of_match_table = xscaler_of_id_table, + }, + .probe = xscaler_probe, + .remove = xscaler_remove, +}; + +module_platform_driver(xscaler_driver); + +MODULE_DESCRIPTION("Xilinx Scaler Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-scenechange-channel.c b/drivers/media/platform/xilinx/xilinx-scenechange-channel.c new file mode 100644 index 000000000000..852191ac6500 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-scenechange-channel.c @@ -0,0 +1,352 @@ +//SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx Scene Change Detection driver + * + * Copyright (C) 2018 Xilinx, Inc. + * + * Authors: Anand Ashok Dumbre <anand.ashok.dumbre@xilinx.com> + * Satish Kumar Nagireddy <satish.nagireddy.nagireddy@xilinx.com> + */ + +#include <linux/of.h> +#include <linux/xilinx-v4l2-events.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-event.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-scenechange.h" +#include "xilinx-vip.h" + +#define XSCD_MAX_WIDTH 3840 +#define XSCD_MAX_HEIGHT 2160 +#define XSCD_MIN_WIDTH 640 +#define XSCD_MIN_HEIGHT 480 + +#define XSCD_V_SUBSAMPLING 16 +#define XSCD_BYTE_ALIGN 16 +#define MULTIPLICATION_FACTOR 100 +#define SCENE_CHANGE_THRESHOLD 0.5 + +#define XSCD_SCENE_CHANGE 1 +#define XSCD_NO_SCENE_CHANGE 0 + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int xscd_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + return 0; +} + +static int xscd_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + return 0; +} + +static struct v4l2_mbus_framefmt * +__xscd_get_pad_format(struct xscd_chan *chan, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&chan->subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &chan->format; + default: + return NULL; + } + return NULL; +} + +static int xscd_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xscd_chan *chan = to_xscd_chan(subdev); + + fmt->format = *__xscd_get_pad_format(chan, cfg, fmt->pad, fmt->which); + return 0; +} + +static int xscd_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xscd_chan *chan = to_xscd_chan(subdev); + struct v4l2_mbus_framefmt *format; + + format = __xscd_get_pad_format(chan, cfg, fmt->pad, fmt->which); + format->width = clamp_t(unsigned int, fmt->format.width, + XSCD_MIN_WIDTH, XSCD_MAX_WIDTH); + format->height = clamp_t(unsigned int, fmt->format.height, + XSCD_MIN_HEIGHT, XSCD_MAX_HEIGHT); + format->code = fmt->format.code; + fmt->format = *format; + + return 0; +} + +static int xscd_chan_get_vid_fmt(u32 media_bus_fmt, bool memory_based) +{ + u32 vid_fmt; + + if (memory_based) { + switch (media_bus_fmt) { + case MEDIA_BUS_FMT_VYYUYY8_1X24: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VUY8_1X24: + vid_fmt = XSCD_VID_FMT_Y8; + break; + case MEDIA_BUS_FMT_VYYUYY10_4X20: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_VUY10_1X30: + vid_fmt = XSCD_VID_FMT_Y10; + break; + default: + vid_fmt = XSCD_VID_FMT_Y8; + } + + return vid_fmt; + } + + /* Streaming based */ + switch (media_bus_fmt) { + case MEDIA_BUS_FMT_VYYUYY8_1X24: + case MEDIA_BUS_FMT_VYYUYY10_4X20: + vid_fmt = XSCD_VID_FMT_YUV_420; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + vid_fmt = XSCD_VID_FMT_YUV_422; + break; + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_VUY10_1X30: + vid_fmt = XSCD_VID_FMT_YUV_444; + break; + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RBG101010_1X30: + vid_fmt = XSCD_VID_FMT_RGB; + break; + default: + vid_fmt = XSCD_VID_FMT_YUV_420; + } + + return vid_fmt; +} + +/** + * xscd_chan_configure_params - Program parameters to HW registers + * @chan: Driver specific channel struct pointer + */ +static void xscd_chan_configure_params(struct xscd_chan *chan) +{ + u32 vid_fmt, stride; + + xscd_write(chan->iomem, XSCD_WIDTH_OFFSET, chan->format.width); + + /* Stride is required only for memory based IP, not for streaming IP */ + if (chan->xscd->memory_based) { + stride = roundup(chan->format.width, XSCD_BYTE_ALIGN); + xscd_write(chan->iomem, XSCD_STRIDE_OFFSET, stride); + } + + xscd_write(chan->iomem, XSCD_HEIGHT_OFFSET, chan->format.height); + + /* Hardware video format */ + vid_fmt = xscd_chan_get_vid_fmt(chan->format.code, + chan->xscd->memory_based); + xscd_write(chan->iomem, XSCD_VID_FMT_OFFSET, vid_fmt); + + /* + * This is the vertical subsampling factor of the input image. Instead + * of sampling every line to calculate the histogram, IP uses this + * register value to sample only specific lines of the frame. + */ + xscd_write(chan->iomem, XSCD_SUBSAMPLE_OFFSET, XSCD_V_SUBSAMPLING); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ +static int xscd_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xscd_chan *chan = to_xscd_chan(subdev); + + if (enable) + xscd_chan_configure_params(chan); + + xscd_dma_enable_channel(&chan->dmachan, enable); + return 0; +} + +static int xscd_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + int ret; + struct xscd_chan *chan = to_xscd_chan(sd); + + mutex_lock(&chan->lock); + + switch (sub->type) { + case V4L2_EVENT_XLNXSCD: + ret = v4l2_event_subscribe(fh, sub, 1, NULL); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&chan->lock); + + return ret; +} + +static int xscd_unsubscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + int ret; + struct xscd_chan *chan = to_xscd_chan(sd); + + mutex_lock(&chan->lock); + ret = v4l2_event_unsubscribe(fh, sub); + mutex_unlock(&chan->lock); + + return ret; +} + +static int xscd_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static int xscd_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static const struct v4l2_subdev_core_ops xscd_core_ops = { + .subscribe_event = xscd_subscribe_event, + .unsubscribe_event = xscd_unsubscribe_event +}; + +static struct v4l2_subdev_video_ops xscd_video_ops = { + .s_stream = xscd_s_stream, +}; + +static struct v4l2_subdev_pad_ops xscd_pad_ops = { + .enum_mbus_code = xscd_enum_mbus_code, + .enum_frame_size = xscd_enum_frame_size, + .get_fmt = xscd_get_format, + .set_fmt = xscd_set_format, +}; + +static struct v4l2_subdev_ops xscd_ops = { + .core = &xscd_core_ops, + .video = &xscd_video_ops, + .pad = &xscd_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xscd_internal_ops = { + .open = xscd_open, + .close = xscd_close, +}; + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static const struct media_entity_operations xscd_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +void xscd_chan_event_notify(struct xscd_chan *chan) +{ + u32 *eventdata; + u32 sad, scd_threshold; + + sad = xscd_read(chan->iomem, XSCD_SAD_OFFSET); + sad = (sad * XSCD_V_SUBSAMPLING * MULTIPLICATION_FACTOR) / + (chan->format.width * chan->format.height); + eventdata = (u32 *)&chan->event.u.data; + scd_threshold = SCENE_CHANGE_THRESHOLD * MULTIPLICATION_FACTOR; + + if (sad > scd_threshold) + eventdata[0] = XSCD_SCENE_CHANGE; + else + eventdata[0] = XSCD_NO_SCENE_CHANGE; + + chan->event.type = V4L2_EVENT_XLNXSCD; + v4l2_subdev_notify_event(&chan->subdev, &chan->event); +} + +/** + * xscd_chan_init - Initialize the V4L2 subdev for a channel + * @xscd: Pointer to the SCD device structure + * @chan_id: Channel id + * @node: device node + * + * Return: '0' on success and failure value on error + */ +int xscd_chan_init(struct xscd_device *xscd, unsigned int chan_id, + struct device_node *node) +{ + struct xscd_chan *chan = &xscd->chans[chan_id]; + struct v4l2_subdev *subdev; + unsigned int num_pads; + int ret; + + mutex_init(&chan->lock); + chan->xscd = xscd; + chan->id = chan_id; + chan->iomem = chan->xscd->iomem + chan->id * XSCD_CHAN_OFFSET; + + /* Initialize V4L2 subdevice and media entity */ + subdev = &chan->subdev; + v4l2_subdev_init(subdev, &xscd_ops); + subdev->dev = chan->xscd->dev; + subdev->fwnode = of_fwnode_handle(node); + subdev->internal_ops = &xscd_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "xlnx-scdchan.%u", + chan_id); + v4l2_set_subdevdata(subdev, chan); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + + /* Initialize default format */ + chan->format.code = MEDIA_BUS_FMT_VYYUYY8_1X24; + chan->format.field = V4L2_FIELD_NONE; + chan->format.width = XSCD_MAX_WIDTH; + chan->format.height = XSCD_MAX_HEIGHT; + + /* Initialize media pads */ + num_pads = xscd->memory_based ? 1 : 2; + + chan->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + if (!xscd->memory_based) + chan->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&subdev->entity, num_pads, chan->pads); + if (ret < 0) + goto error; + + subdev->entity.ops = &xscd_media_ops; + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(chan->xscd->dev, "failed to register subdev\n"); + goto error; + } + + dev_info(chan->xscd->dev, "Scene change detection channel found!\n"); + return 0; + +error: + media_entity_cleanup(&subdev->entity); + return ret; +} diff --git a/drivers/media/platform/xilinx/xilinx-scenechange-dma.c b/drivers/media/platform/xilinx/xilinx-scenechange-dma.c new file mode 100644 index 000000000000..58437a769605 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-scenechange-dma.c @@ -0,0 +1,554 @@ +//SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx Scene Change Detection DMA driver + * + * Copyright (C) 2018 Xilinx, Inc. + * + * Authors: Anand Ashok Dumbre <anand.ashok.dumbre@xilinx.com> + * Satish Kumar Nagireddy <satish.nagireddy.nagireddy@xilinx.com> + */ + +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/of_dma.h> +#include <linux/slab.h> + +#include "../../../dma/dmaengine.h" + +#include "xilinx-scenechange.h" + +/** + * xscd_dma_start - Start the SCD core + * @xscd: The SCD device + * @channels: Bitmask of enabled channels + */ +static void xscd_dma_start(struct xscd_device *xscd, unsigned int channels) +{ + xscd_write(xscd->iomem, XSCD_IE_OFFSET, XSCD_IE_AP_DONE); + xscd_write(xscd->iomem, XSCD_GIE_OFFSET, XSCD_GIE_EN); + xscd_write(xscd->iomem, XSCD_CHAN_EN_OFFSET, channels); + + xscd_set(xscd->iomem, XSCD_CTRL_OFFSET, + xscd->memory_based ? XSCD_CTRL_AP_START + : XSCD_CTRL_AP_START | + XSCD_CTRL_AUTO_RESTART); + + xscd->running = true; +} + +/** + * xscd_dma_stop - Stop the SCD core + * @xscd: The SCD device + */ +static void xscd_dma_stop(struct xscd_device *xscd) +{ + xscd_clr(xscd->iomem, XSCD_CTRL_OFFSET, + xscd->memory_based ? XSCD_CTRL_AP_START + : XSCD_CTRL_AP_START | + XSCD_CTRL_AUTO_RESTART); + + xscd->running = false; +} + +/** + * xscd_dma_setup_channel - Setup a channel for transfer + * @chan: Driver specific channel struct pointer + * + * Return: 1 if the channel starts to run for a new transfer. Otherwise, 0. + */ +static int xscd_dma_setup_channel(struct xscd_dma_chan *chan) +{ + struct xscd_dma_tx_descriptor *desc; + + if (!chan->enabled) + return 0; + + if (list_empty(&chan->pending_list)) + return 0; + + desc = list_first_entry(&chan->pending_list, + struct xscd_dma_tx_descriptor, node); + list_del(&desc->node); + + xscd_write(chan->iomem, XSCD_ADDR_OFFSET, desc->sw.luma_plane_addr); + chan->active_desc = desc; + + return 1; +} + +/** + * xscd_dma_kick - Start a run of the SCD core if channels are ready + * @xscd: The SCD device + * + * This function starts a single run of the SCD core when all the following + * conditions are met: + * + * - The SCD is not currently running + * - At least one channel is enabled and has buffers available + * + * It can be used to start the SCD when a buffer is queued, when a channel + * starts streaming, or to start the next run. Calling this function is only + * valid for memory-based mode and is not permitted for stream-based mode. + * + * The running state for all channels is updated. Channels that are being + * stopped are signalled through the channel wait queue. + * + * The function must be called with the xscd_device lock held. + */ +static void xscd_dma_kick(struct xscd_device *xscd) +{ + unsigned int channels = 0; + unsigned int i; + + lockdep_assert_held(&xscd->lock); + + if (xscd->running) + return; + + for (i = 0; i < xscd->num_streams; i++) { + struct xscd_dma_chan *chan = xscd->channels[i]; + unsigned long flags; + unsigned int running; + bool stopped; + + spin_lock_irqsave(&chan->lock, flags); + running = xscd_dma_setup_channel(chan); + stopped = chan->running && !running; + chan->running = running; + spin_unlock_irqrestore(&chan->lock, flags); + + channels |= running << chan->id; + if (stopped) + wake_up(&chan->wait); + } + + if (channels) + xscd_dma_start(xscd, channels); + else + xscd_dma_stop(xscd); +} + +/** + * xscd_dma_enable_channel - Enable/disable a channel + * @chan: Driver specific channel struct pointer + * @enable: True to enable the channel, false to disable it + * + * This function enables or disable a channel. When operating in memory-based + * mode, enabling a channel kicks processing if buffers are available for any + * enabled channel and the SCD core is idle. When operating in stream-based + * mode, the SCD core is started or stopped synchronously when then channel is + * enabled or disabled. + * + * This function must be called in non-atomic, non-interrupt context. + */ +void xscd_dma_enable_channel(struct xscd_dma_chan *chan, bool enable) +{ + struct xscd_device *xscd = chan->xscd; + + if (enable) { + /* + * FIXME: Don't set chan->enabled to false here, it will be + * done in xscd_dma_terminate_all(). This works around a bug + * introduced in commit 2e77607047c6 ("xilinx: v4l2: dma: Add + * multiple output support") that stops all channels when the + * first one is stopped, even though they are part of + * independent pipelines. This workaround should be safe as + * long as dmaengine_terminate_all() is called after + * xvip_pipeline_set_stream(). + */ + spin_lock_irq(&chan->lock); + chan->enabled = true; + spin_unlock_irq(&chan->lock); + } + + if (xscd->memory_based) { + if (enable) { + spin_lock_irq(&xscd->lock); + xscd_dma_kick(xscd); + spin_unlock_irq(&xscd->lock); + } + } else { + if (enable) + xscd_dma_start(xscd, BIT(chan->id)); + else + xscd_dma_stop(xscd); + } +} + +/** + * xscd_dma_irq_handler - scdma Interrupt handler + * @xscd: Pointer to the SCD device structure + */ +void xscd_dma_irq_handler(struct xscd_device *xscd) +{ + unsigned int i; + + /* + * Mark the active descriptors as complete, move them to the done list + * and schedule the tasklet to clean them up. + */ + for (i = 0; i < xscd->num_streams; ++i) { + struct xscd_dma_chan *chan = xscd->channels[i]; + struct xscd_dma_tx_descriptor *desc = chan->active_desc; + + if (!desc) + continue; + + dma_cookie_complete(&desc->async_tx); + xscd_chan_event_notify(&xscd->chans[i]); + + spin_lock(&chan->lock); + list_add_tail(&desc->node, &chan->done_list); + chan->active_desc = NULL; + spin_unlock(&chan->lock); + + tasklet_schedule(&chan->tasklet); + } + + /* Start the next run, if any. */ + spin_lock(&xscd->lock); + xscd->running = false; + xscd_dma_kick(xscd); + spin_unlock(&xscd->lock); +} + +/* ----------------------------------------------------------------------------- + * DMA Engine + */ + +/** + * xscd_dma_tx_submit - Submit DMA transaction + * @tx: Async transaction descriptor + * + * Return: cookie value on success and failure value on error + */ +static dma_cookie_t xscd_dma_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct xscd_dma_tx_descriptor *desc = to_xscd_dma_tx_descriptor(tx); + struct xscd_dma_chan *chan = to_xscd_dma_chan(tx->chan); + dma_cookie_t cookie; + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + cookie = dma_cookie_assign(tx); + list_add_tail(&desc->node, &chan->pending_list); + spin_unlock_irqrestore(&chan->lock, flags); + + return cookie; +} + +/** + * xscd_dma_free_desc_list - Free descriptors list + * @chan: Driver specific dma channel + * @list: List to parse and delete the descriptor + */ +static void xscd_dma_free_desc_list(struct xscd_dma_chan *chan, + struct list_head *list) +{ + struct xscd_dma_tx_descriptor *desc, *next; + + list_for_each_entry_safe(desc, next, list, node) { + list_del(&desc->node); + kfree(desc); + } +} + +/** + * xscd_dma_free_descriptors - Free channel descriptors + * @chan: Driver specific dma channel + */ +static void xscd_dma_free_descriptors(struct xscd_dma_chan *chan) +{ + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + + xscd_dma_free_desc_list(chan, &chan->pending_list); + xscd_dma_free_desc_list(chan, &chan->done_list); + kfree(chan->active_desc); + + chan->active_desc = NULL; + INIT_LIST_HEAD(&chan->pending_list); + INIT_LIST_HEAD(&chan->done_list); + + spin_unlock_irqrestore(&chan->lock, flags); +} + +/** + * scd_dma_chan_desc_cleanup - Clean channel descriptors + * @chan: Driver specific dma channel + */ +static void xscd_dma_chan_desc_cleanup(struct xscd_dma_chan *chan) +{ + struct xscd_dma_tx_descriptor *desc, *next; + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + + list_for_each_entry_safe(desc, next, &chan->done_list, node) { + dma_async_tx_callback callback; + void *callback_param; + + list_del(&desc->node); + + /* Run the link descriptor callback function */ + callback = desc->async_tx.callback; + callback_param = desc->async_tx.callback_param; + if (callback) { + spin_unlock_irqrestore(&chan->lock, flags); + callback(callback_param); + spin_lock_irqsave(&chan->lock, flags); + } + + kfree(desc); + } + + spin_unlock_irqrestore(&chan->lock, flags); +} + +/** + * xscd_dma_dma_prep_interleaved - prepare a descriptor for a + * DMA_SLAVE transaction + * @dchan: DMA channel + * @xt: Interleaved template pointer + * @flags: transfer ack flags + * + * Return: Async transaction descriptor on success and NULL on failure + */ +static struct dma_async_tx_descriptor * +xscd_dma_prep_interleaved(struct dma_chan *dchan, + struct dma_interleaved_template *xt, + unsigned long flags) +{ + struct xscd_dma_chan *chan = to_xscd_dma_chan(dchan); + struct xscd_dma_tx_descriptor *desc; + struct xscd_dma_desc *sw; + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return NULL; + + dma_async_tx_descriptor_init(&desc->async_tx, &chan->common); + desc->async_tx.tx_submit = xscd_dma_tx_submit; + async_tx_ack(&desc->async_tx); + + sw = &desc->sw; + sw->vsize = xt->numf; + sw->hsize = xt->sgl[0].size; + sw->stride = xt->sgl[0].size + xt->sgl[0].icg; + sw->luma_plane_addr = xt->src_start; + + return &desc->async_tx; +} + +static bool xscd_dma_is_running(struct xscd_dma_chan *chan) +{ + bool running; + + spin_lock_irq(&chan->lock); + running = chan->running; + spin_unlock_irq(&chan->lock); + + return running; +} + +/** + * xscd_dma_terminate_all - Halt the channel and free descriptors + * @dchan: Driver specific dma channel pointer + * + * Return: 0 + */ +static int xscd_dma_terminate_all(struct dma_chan *dchan) +{ + struct xscd_dma_chan *chan = to_xscd_dma_chan(dchan); + int ret; + + spin_lock_irq(&chan->lock); + chan->enabled = false; + spin_unlock_irq(&chan->lock); + + /* Wait for any on-going transfer to complete. */ + ret = wait_event_timeout(chan->wait, !xscd_dma_is_running(chan), + msecs_to_jiffies(100)); + WARN_ON(ret == 0); + + xscd_dma_free_descriptors(chan); + return 0; +} + +/** + * xscd_dma_issue_pending - Issue pending transactions + * @dchan: DMA channel + */ +static void xscd_dma_issue_pending(struct dma_chan *dchan) +{ + struct xscd_dma_chan *chan = to_xscd_dma_chan(dchan); + struct xscd_device *xscd = chan->xscd; + unsigned long flags; + + spin_lock_irqsave(&xscd->lock, flags); + xscd_dma_kick(xscd); + spin_unlock_irqrestore(&xscd->lock, flags); +} + +static enum dma_status xscd_dma_tx_status(struct dma_chan *dchan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + return dma_cookie_status(dchan, cookie, txstate); +} + +/** + * xscd_dma_free_chan_resources - Free channel resources + * @dchan: DMA channel + */ +static void xscd_dma_free_chan_resources(struct dma_chan *dchan) +{ + struct xscd_dma_chan *chan = to_xscd_dma_chan(dchan); + + xscd_dma_free_descriptors(chan); +} + +/** + * xscd_dma_do_tasklet - Schedule completion tasklet + * @data: Pointer to the Xilinx scdma channel structure + */ +static void xscd_dma_do_tasklet(unsigned long data) +{ + struct xscd_dma_chan *chan = (struct xscd_dma_chan *)data; + + xscd_dma_chan_desc_cleanup(chan); +} + +/** + * xscd_dma_alloc_chan_resources - Allocate channel resources + * @dchan: DMA channel + * + * Return: '0' on success and failure value on error + */ +static int xscd_dma_alloc_chan_resources(struct dma_chan *dchan) +{ + dma_cookie_init(dchan); + return 0; +} + +/** + * of_scdma_xilinx_xlate - Translation function + * @dma_spec: Pointer to DMA specifier as found in the device tree + * @ofdma: Pointer to DMA controller data + * + * Return: DMA channel pointer on success and NULL on error + */ +static struct dma_chan *of_scdma_xilinx_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct xscd_device *xscd = ofdma->of_dma_data; + u32 chan_id = dma_spec->args[0]; + + if (chan_id >= xscd->num_streams) + return NULL; + + if (!xscd->channels[chan_id]) + return NULL; + + return dma_get_slave_channel(&xscd->channels[chan_id]->common); +} + +static void xscd_dma_chan_init(struct xscd_device *xscd, int chan_id) +{ + struct xscd_dma_chan *chan = &xscd->chans[chan_id].dmachan; + + chan->id = chan_id; + chan->iomem = xscd->iomem + chan->id * XSCD_CHAN_OFFSET; + chan->xscd = xscd; + + xscd->channels[chan->id] = chan; + + spin_lock_init(&chan->lock); + INIT_LIST_HEAD(&chan->pending_list); + INIT_LIST_HEAD(&chan->done_list); + tasklet_init(&chan->tasklet, xscd_dma_do_tasklet, + (unsigned long)chan); + init_waitqueue_head(&chan->wait); + + chan->common.device = &xscd->dma_device; + list_add_tail(&chan->common.device_node, &xscd->dma_device.channels); +} + +/** + * xscd_dma_chan_remove - Per Channel remove function + * @chan: Driver specific DMA channel + */ +static void xscd_dma_chan_remove(struct xscd_dma_chan *chan) +{ + list_del(&chan->common.device_node); +} + +/** + * xscd_dma_init - Initialize the SCD DMA engine + * @xscd: Pointer to the SCD device structure + * + * Return: '0' on success and failure value on error + */ +int xscd_dma_init(struct xscd_device *xscd) +{ + struct dma_device *ddev = &xscd->dma_device; + unsigned int chan_id; + int ret; + + /* Initialize the DMA engine */ + ddev->dev = xscd->dev; + dma_set_mask(xscd->dev, DMA_BIT_MASK(32)); + + INIT_LIST_HEAD(&ddev->channels); + dma_cap_set(DMA_SLAVE, ddev->cap_mask); + dma_cap_set(DMA_PRIVATE, ddev->cap_mask); + ddev->device_alloc_chan_resources = xscd_dma_alloc_chan_resources; + ddev->device_free_chan_resources = xscd_dma_free_chan_resources; + ddev->device_tx_status = xscd_dma_tx_status; + ddev->device_issue_pending = xscd_dma_issue_pending; + ddev->device_terminate_all = xscd_dma_terminate_all; + ddev->device_prep_interleaved_dma = xscd_dma_prep_interleaved; + + for (chan_id = 0; chan_id < xscd->num_streams; chan_id++) + xscd_dma_chan_init(xscd, chan_id); + + ret = dma_async_device_register(ddev); + if (ret) { + dev_err(xscd->dev, "failed to register the dma device\n"); + goto error; + } + + ret = of_dma_controller_register(xscd->dev->of_node, + of_scdma_xilinx_xlate, xscd); + if (ret) { + dev_err(xscd->dev, "failed to register DMA to DT DMA helper\n"); + goto error_of_dma; + } + + dev_info(xscd->dev, "Xilinx Scene Change DMA is initialized!\n"); + return 0; + +error_of_dma: + dma_async_device_unregister(ddev); + +error: + for (chan_id = 0; chan_id < xscd->num_streams; chan_id++) + xscd_dma_chan_remove(xscd->channels[chan_id]); + + return ret; +} + +/** + * xscd_dma_cleanup - Clean up the SCD DMA engine + * @xscd: Pointer to the SCD device structure + * + * This function is the counterpart of xscd_dma_init() and cleans up the + * resources related to the DMA engine. + */ +void xscd_dma_cleanup(struct xscd_device *xscd) +{ + dma_async_device_unregister(&xscd->dma_device); + of_dma_controller_free(xscd->dev->of_node); +} diff --git a/drivers/media/platform/xilinx/xilinx-scenechange.c b/drivers/media/platform/xilinx/xilinx-scenechange.c new file mode 100644 index 000000000000..9135355934fe --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-scenechange.c @@ -0,0 +1,191 @@ +//SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx Scene Change Detection driver + * + * Copyright (C) 2018 Xilinx, Inc. + * + * Authors: Anand Ashok Dumbre <anand.ashok.dumbre@xilinx.com> + * Satish Kumar Nagireddy <satish.nagireddy.nagireddy@xilinx.com> + */ + +#include <linux/clk.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include "xilinx-scenechange.h" + +#define XSCD_RESET_DEASSERT (0) +#define XSCD_RESET_ASSERT (1) + +static irqreturn_t xscd_irq_handler(int irq, void *data) +{ + struct xscd_device *xscd = (struct xscd_device *)data; + u32 status; + + status = xscd_read(xscd->iomem, XSCD_ISR_OFFSET); + if (!(status & XSCD_IE_AP_DONE)) + return IRQ_NONE; + + xscd_write(xscd->iomem, XSCD_ISR_OFFSET, XSCD_IE_AP_DONE); + + if (xscd->memory_based) + xscd_dma_irq_handler(xscd); + else + xscd_chan_event_notify(&xscd->chans[0]); + + return IRQ_HANDLED; +} + +static int xscd_init_resources(struct xscd_device *xscd) +{ + struct platform_device *pdev = to_platform_device(xscd->dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xscd->iomem = devm_ioremap_resource(xscd->dev, res); + if (IS_ERR(xscd->iomem)) + return PTR_ERR(xscd->iomem); + + xscd->irq = platform_get_irq(pdev, 0); + if (xscd->irq < 0) { + dev_err(xscd->dev, "No valid irq found\n"); + return -EINVAL; + } + + xscd->clk = devm_clk_get(xscd->dev, NULL); + if (IS_ERR(xscd->clk)) + return PTR_ERR(xscd->clk); + + clk_prepare_enable(xscd->clk); + return 0; +} + +static int xscd_parse_of(struct xscd_device *xscd) +{ + struct device *dev = xscd->dev; + struct device_node *node = xscd->dev->of_node; + int ret; + + xscd->memory_based = of_property_read_bool(node, "xlnx,memorybased"); + xscd->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(xscd->rst_gpio)) { + if (PTR_ERR(xscd->rst_gpio) != -EPROBE_DEFER) + dev_err(dev, "Reset GPIO not setup in DT\n"); + + return PTR_ERR(xscd->rst_gpio); + } + + ret = of_property_read_u32(node, "xlnx,numstreams", + &xscd->num_streams); + if (ret < 0) + return ret; + + if (!xscd->memory_based && xscd->num_streams != 1) { + dev_err(dev, "Stream-based mode only supports one stream\n"); + return -EINVAL; + } + + return 0; +} + +static int xscd_probe(struct platform_device *pdev) +{ + struct xscd_device *xscd; + struct device_node *subdev_node; + unsigned int id; + int ret; + + xscd = devm_kzalloc(&pdev->dev, sizeof(*xscd), GFP_KERNEL); + if (!xscd) + return -ENOMEM; + + spin_lock_init(&xscd->lock); + + xscd->dev = &pdev->dev; + platform_set_drvdata(pdev, xscd); + + ret = xscd_parse_of(xscd); + if (ret < 0) + return ret; + + ret = xscd_init_resources(xscd); + if (ret < 0) + return ret; + + /* Reset Scene Change Detection IP */ + gpiod_set_value_cansleep(xscd->rst_gpio, XSCD_RESET_ASSERT); + gpiod_set_value_cansleep(xscd->rst_gpio, XSCD_RESET_DEASSERT); + + /* Initialize the channels. */ + xscd->chans = devm_kcalloc(xscd->dev, xscd->num_streams, + sizeof(*xscd->chans), GFP_KERNEL); + if (!xscd->chans) + return -ENOMEM; + + id = 0; + for_each_child_of_node(xscd->dev->of_node, subdev_node) { + if (id >= xscd->num_streams) { + dev_warn(&pdev->dev, + "Too many channels, limiting to %u\n", + xscd->num_streams); + of_node_put(subdev_node); + break; + } + + ret = xscd_chan_init(xscd, id, subdev_node); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to initialize channel %u\n", + id); + return ret; + } + + id++; + } + + /* Initialize the DMA engine. */ + ret = xscd_dma_init(xscd); + if (ret < 0) + dev_err(&pdev->dev, "Failed to initialize the DMA\n"); + + ret = devm_request_irq(xscd->dev, xscd->irq, xscd_irq_handler, + IRQF_SHARED, dev_name(xscd->dev), xscd); + if (ret < 0) + dev_err(&pdev->dev, "Failed to request IRQ\n"); + + dev_info(xscd->dev, "scene change detect device found!\n"); + return 0; +} + +static int xscd_remove(struct platform_device *pdev) +{ + struct xscd_device *xscd = platform_get_drvdata(pdev); + + xscd_dma_cleanup(xscd); + clk_disable_unprepare(xscd->clk); + + return 0; +} + +static const struct of_device_id xscd_of_id_table[] = { + { .compatible = "xlnx,v-scd" }, + { } +}; +MODULE_DEVICE_TABLE(of, xscd_of_id_table); + +static struct platform_driver xscd_driver = { + .driver = { + .name = "xilinx-scd", + .of_match_table = xscd_of_id_table, + }, + .probe = xscd_probe, + .remove = xscd_remove, +}; + +module_platform_driver(xscd_driver); + +MODULE_AUTHOR("Xilinx Inc."); +MODULE_DESCRIPTION("Xilinx Scene Change Detection"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-scenechange.h b/drivers/media/platform/xilinx/xilinx-scenechange.h new file mode 100644 index 000000000000..1573bf825217 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-scenechange.h @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Xilinx Scene Change Detection driver + * + * Copyright (C) 2018 Xilinx, Inc. + * + * Authors: Anand Ashok Dumbre <anand.ashok.dumbre@xilinx.com> + * Satish Kumar Nagireddy <satish.nagireddy.nagireddy@xilinx.com> + */ + +#ifndef _XILINX_SCENECHANGE_H_ +#define _XILINX_SCENECHANGE_H_ + +#include <linux/bitops.h> +#include <linux/dmaengine.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/wait.h> + +#include <media/v4l2-subdev.h> + +struct clk; +struct device; +struct device_node; +struct gpio_desc; + +/* Register/Descriptor Offsets */ +#define XSCD_CTRL_OFFSET 0x000 +#define XSCD_CTRL_AP_START BIT(0) +#define XSCD_CTRL_AP_DONE BIT(1) +#define XSCD_CTRL_AP_IDLE BIT(2) +#define XSCD_CTRL_AP_READY BIT(3) +#define XSCD_CTRL_AUTO_RESTART BIT(7) + +#define XSCD_GIE_OFFSET 0x004 +#define XSCD_GIE_EN BIT(0) + +#define XSCD_IE_OFFSET 0x008 +#define XSCD_IE_AP_DONE BIT(0) +#define XSCD_IE_AP_READY BIT(1) + +#define XSCD_ISR_OFFSET 0x00c +#define XSCD_WIDTH_OFFSET 0x010 +#define XSCD_HEIGHT_OFFSET 0x018 +#define XSCD_STRIDE_OFFSET 0x020 +#define XSCD_VID_FMT_OFFSET 0x028 +#define XSCD_VID_FMT_RGB 0 +#define XSCD_VID_FMT_YUV_444 1 +#define XSCD_VID_FMT_YUV_422 2 +#define XSCD_VID_FMT_YUV_420 3 +#define XSCD_VID_FMT_Y8 24 +#define XSCD_VID_FMT_Y10 25 + +#define XSCD_SUBSAMPLE_OFFSET 0x030 +#define XSCD_SAD_OFFSET 0x038 +#define XSCD_ADDR_OFFSET 0x040 +#define XSCD_CHAN_OFFSET 0x100 +#define XSCD_CHAN_EN_OFFSET 0x780 + +#define XSCD_MAX_CHANNELS 8 + +/****************************** PROTOTYPES ************************************/ + +struct xscd_device; + +/** + * struct xscd_dma_desc - DMA channel + * @luma_plane_addr: Luma plane buffer address + * @vsize: width of the luma frame + * @hsize: height of the luma frame + * @stride: stride of the luma frame + */ +struct xscd_dma_desc { + dma_addr_t luma_plane_addr; + u32 vsize; + u32 hsize; + u32 stride; +}; + +/** + * struct xscd_dma_tx_descriptor - Per Transaction structure + * @async_tx: Async transaction descriptor + * @sw: Software Descriptor + * @node: Node in the channel descriptor list + */ +struct xscd_dma_tx_descriptor { + struct dma_async_tx_descriptor async_tx; + struct xscd_dma_desc sw; + struct list_head node; +}; + +static inline struct xscd_dma_tx_descriptor * +to_xscd_dma_tx_descriptor(struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct xscd_dma_tx_descriptor, async_tx); +} + +/** + * struct xscd_dma_chan - DMA Channel structure + * @xscd: SCD device + * @iomem: I/O memory address of the channel registers + * @id: scene change channel ID + * @common: DMA common channel + * @tasklet: Cleanup work after irq + * @lock: Protects pending_list, done_list, active_desc, enabled and running + * @pending_list: Descriptors waiting + * @done_list: Complete descriptors + * @active_desc: Currently active buffer being read/written to + * @enabled: Channel is enabled + * @running: Channel is running + * @wait: Wait queue to wait for the channel to stop + */ +struct xscd_dma_chan { + struct xscd_device *xscd; + void __iomem *iomem; + unsigned int id; + + struct dma_chan common; + struct tasklet_struct tasklet; + + spinlock_t lock; + struct list_head pending_list; + struct list_head done_list; + struct xscd_dma_tx_descriptor *active_desc; + unsigned int enabled; + unsigned int running; + wait_queue_head_t wait; +}; + +static inline struct xscd_dma_chan *to_xscd_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct xscd_dma_chan, common); +} + +/** + * struct xscd_chan - Video Stream structure + * @id: scene change channel ID + * @iomem: I/O memory address of the channel registers + * @xscd: SCD device + * @subdev: V4L2 subdevice + * @pads: media pads + * @format: active V4L2 media bus format for the pad + * @event: scene change event + * @dmachan: dma channel part of the scenechange stream + * @lock: lock to protect active stream count variable + */ +struct xscd_chan { + int id; + void __iomem *iomem; + struct xscd_device *xscd; + struct v4l2_subdev subdev; + struct media_pad pads[2]; + struct v4l2_mbus_framefmt format; + struct v4l2_event event; + struct xscd_dma_chan dmachan; + + /* Lock to protect active stream count */ + struct mutex lock; +}; + +static inline struct xscd_chan *to_xscd_chan(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xscd_chan, subdev); +} + +/** + * struct xscd_device - Xilinx Scene Change Detection device structure + * @dev: (OF) device + * @iomem: device I/O register space remapped to kernel virtual memory + * @rst_gpio: reset GPIO + * @clk: video core clock + * @irq: Device IRQ + * @memory_based: Flag to identify memory based mode + * @num_streams: Number of streams in the design + * @chans: video stream instances + * @dma_device: DMA device structure + * @channels: DMA channels + * @lock: Protects the running field + * @running: True when the SCD core is running + */ +struct xscd_device { + struct device *dev; + void __iomem *iomem; + struct gpio_desc *rst_gpio; + struct clk *clk; + int irq; + + u8 memory_based; + int num_streams; + + struct xscd_chan *chans; + + struct dma_device dma_device; + struct xscd_dma_chan *channels[XSCD_MAX_CHANNELS]; + + /* This lock is to protect the running field */ + spinlock_t lock; + u8 running; +}; + +/* + * Register related operations + */ +static inline u32 xscd_read(void __iomem *iomem, u32 addr) +{ + return ioread32(iomem + addr); +} + +static inline void xscd_write(void __iomem *iomem, u32 addr, u32 value) +{ + iowrite32(value, iomem + addr); +} + +static inline void xscd_clr(void __iomem *iomem, u32 addr, u32 clr) +{ + xscd_write(iomem, addr, xscd_read(iomem, addr) & ~clr); +} + +static inline void xscd_set(void __iomem *iomem, u32 addr, u32 set) +{ + xscd_write(iomem, addr, xscd_read(iomem, addr) | set); +} + +void xscd_dma_enable_channel(struct xscd_dma_chan *chan, bool enable); +void xscd_dma_irq_handler(struct xscd_device *xscd); +int xscd_dma_init(struct xscd_device *xscd); +void xscd_dma_cleanup(struct xscd_device *xscd); + +void xscd_chan_event_notify(struct xscd_chan *chan); +int xscd_chan_init(struct xscd_device *xscd, unsigned int chan_id, + struct device_node *node); +#endif diff --git a/drivers/media/platform/xilinx/xilinx-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c new file mode 100644 index 000000000000..1ee54ce28d22 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c @@ -0,0 +1,1855 @@ +/* + * Xilinx SDI Rx Subsystem + * + * Copyright (C) 2017 Xilinx, Inc. + * + * Contacts: Vishal Sagar <vsagar@xilinx.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <dt-bindings/media/xilinx-vip.h> +#include <linux/bitops.h> +#include <linux/compiler.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/spinlock_types.h> +#include <linux/types.h> +#include <linux/v4l2-subdev.h> +#include <linux/xilinx-sdirxss.h> +#include <linux/xilinx-v4l2-controls.h> +#include <media/media-entity.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> +#include "xilinx-vip.h" + +/* + * SDI Rx register map, bitmask and offsets + */ +#define XSDIRX_RST_CTRL_REG 0x00 +#define XSDIRX_MDL_CTRL_REG 0x04 +#define XSDIRX_GLBL_IER_REG 0x0C +#define XSDIRX_ISR_REG 0x10 +#define XSDIRX_IER_REG 0x14 +#define XSDIRX_ST352_VALID_REG 0x18 +#define XSDIRX_ST352_DS1_REG 0x1C +#define XSDIRX_ST352_DS3_REG 0x20 +#define XSDIRX_ST352_DS5_REG 0x24 +#define XSDIRX_ST352_DS7_REG 0x28 +#define XSDIRX_ST352_DS9_REG 0x2C +#define XSDIRX_ST352_DS11_REG 0x30 +#define XSDIRX_ST352_DS13_REG 0x34 +#define XSDIRX_ST352_DS15_REG 0x38 +#define XSDIRX_VERSION_REG 0x3C +#define XSDIRX_SS_CONFIG_REG 0x40 +#define XSDIRX_MODE_DET_STAT_REG 0x44 +#define XSDIRX_TS_DET_STAT_REG 0x48 +#define XSDIRX_EDH_STAT_REG 0x4C +#define XSDIRX_EDH_ERRCNT_EN_REG 0x50 +#define XSDIRX_EDH_ERRCNT_REG 0x54 +#define XSDIRX_CRC_ERRCNT_REG 0x58 +#define XSDIRX_VID_LOCK_WINDOW_REG 0x5C +#define XSDIRX_SB_RX_STS_REG 0x60 + +#define XSDIRX_RST_CTRL_SS_EN_MASK BIT(0) +#define XSDIRX_RST_CTRL_SRST_MASK BIT(1) +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK BIT(2) +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK BIT(3) +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK BIT(8) +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK BIT(9) + +#define XSDIRX_MDL_CTRL_FRM_EN_MASK BIT(4) +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK BIT(5) +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK BIT(8) +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK BIT(9) +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK BIT(10) +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK BIT(11) +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK BIT(12) +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK BIT(13) +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK GENMASK(13, 8) + +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET 16 +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK GENMASK(18, 16) + +#define XSDIRX_GLBL_INTR_EN_MASK BIT(0) + +#define XSDIRX_INTR_VIDLOCK_MASK BIT(0) +#define XSDIRX_INTR_VIDUNLOCK_MASK BIT(1) +#define XSDIRX_INTR_OVERFLOW_MASK BIT(9) +#define XSDIRX_INTR_UNDERFLOW_MASK BIT(10) + +#define XSDIRX_INTR_ALL_MASK (XSDIRX_INTR_VIDLOCK_MASK |\ + XSDIRX_INTR_VIDUNLOCK_MASK |\ + XSDIRX_INTR_OVERFLOW_MASK |\ + XSDIRX_INTR_UNDERFLOW_MASK) + +#define XSDIRX_ST352_VALID_DS1_MASK BIT(0) +#define XSDIRX_ST352_VALID_DS3_MASK BIT(1) +#define XSDIRX_ST352_VALID_DS5_MASK BIT(2) +#define XSDIRX_ST352_VALID_DS7_MASK BIT(3) +#define XSDIRX_ST352_VALID_DS9_MASK BIT(4) +#define XSDIRX_ST352_VALID_DS11_MASK BIT(5) +#define XSDIRX_ST352_VALID_DS13_MASK BIT(6) +#define XSDIRX_ST352_VALID_DS15_MASK BIT(7) + +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK GENMASK(2, 0) +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK BIT(3) +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK GENMASK(6, 4) +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET 4 +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK BIT(7) + +#define XSDIRX_ACTIVE_STREAMS_1 0x0 +#define XSDIRX_ACTIVE_STREAMS_2 0x1 +#define XSDIRX_ACTIVE_STREAMS_4 0x2 +#define XSDIRX_ACTIVE_STREAMS_8 0x3 +#define XSDIRX_ACTIVE_STREAMS_16 0x4 + +#define XSDIRX_TS_DET_STAT_LOCKED_MASK BIT(0) +#define XSDIRX_TS_DET_STAT_SCAN_MASK BIT(1) +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET (1) +#define XSDIRX_TS_DET_STAT_FAMILY_MASK GENMASK(7, 4) +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET (4) +#define XSDIRX_TS_DET_STAT_RATE_MASK GENMASK(11, 8) +#define XSDIRX_TS_DET_STAT_RATE_OFFSET (8) + +#define XSDIRX_TS_DET_STAT_RATE_NONE 0x0 +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ 0x2 +#define XSDIRX_TS_DET_STAT_RATE_24HZ 0x3 +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ 0x4 +#define XSDIRX_TS_DET_STAT_RATE_25HZ 0x5 +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ 0x6 +#define XSDIRX_TS_DET_STAT_RATE_30HZ 0x7 +#define XSDIRX_TS_DET_STAT_RATE_48HZ 0x8 +#define XSDIRX_TS_DET_STAT_RATE_50HZ 0x9 +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ 0xA +#define XSDIRX_TS_DET_STAT_RATE_60HZ 0xB + +#define XSDIRX_EDH_STAT_EDH_AP_MASK BIT(0) +#define XSDIRX_EDH_STAT_EDH_FF_MASK BIT(1) +#define XSDIRX_EDH_STAT_EDH_ANC_MASK BIT(2) +#define XSDIRX_EDH_STAT_AP_FLAG_MASK GENMASK(8, 4) +#define XSDIRX_EDH_STAT_FF_FLAG_MASK GENMASK(13, 9) +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK GENMASK(18, 14) +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK GENMASK(22, 19) + +#define XSDIRX_EDH_ERRCNT_COUNT_MASK GENMASK(15, 0) + +#define XSDIRX_CRC_ERRCNT_COUNT_MASK GENMASK(31, 16) +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK GENMASK(15, 0) + +#define XSDIRX_VERSION_REV_MASK GENMASK(7, 0) +#define XSDIRX_VERSION_PATCHID_MASK GENMASK(11, 8) +#define XSDIRX_VERSION_VER_REV_MASK GENMASK(15, 12) +#define XSDIRX_VERSION_VER_MIN_MASK GENMASK(23, 16) +#define XSDIRX_VERSION_VER_MAJ_MASK GENMASK(31, 24) + +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK BIT(1) + +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK BIT(0) +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK BIT(1) +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK BIT(2) +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK BIT(3) + +/* Number of media pads */ +#define XSDIRX_MEDIA_PADS (1) + +#define XSDIRX_DEFAULT_WIDTH (1920) +#define XSDIRX_DEFAULT_HEIGHT (1080) + +#define XSDIRX_MAX_STR_LENGTH 16 + +#define XSDIRXSS_SDI_STD_3G 0 +#define XSDIRXSS_SDI_STD_6G 1 +#define XSDIRXSS_SDI_STD_12G_8DS 2 + +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW 0x3000 + +#define XSDIRX_MODE_HD_MASK 0x0 +#define XSDIRX_MODE_SD_MASK 0x1 +#define XSDIRX_MODE_3G_MASK 0x2 +#define XSDIRX_MODE_6G_MASK 0x4 +#define XSDIRX_MODE_12GI_MASK 0x5 +#define XSDIRX_MODE_12GF_MASK 0x6 + +/* + * Maximum number of events per file handle. + */ +#define XSDIRX_MAX_EVENTS (128) + +/* ST352 related macros */ +#define XST352_PAYLOAD_BYTE_MASK 0xFF +#define XST352_PAYLOAD_BYTE1_SHIFT 0 +#define XST352_PAYLOAD_BYTE2_SHIFT 8 +#define XST352_PAYLOAD_BYTE3_SHIFT 16 +#define XST352_PAYLOAD_BYTE4_SHIFT 24 + +#define XST352_BYTE1_ST292_1x720L_1_5G 0x84 +#define XST352_BYTE1_ST292_1x1080L_1_5G 0x85 +#define XST352_BYTE1_ST425_2008_750L_3GB 0x88 +#define XST352_BYTE1_ST425_2008_1125L_3GA 0x89 +#define XST352_BYTE1_ST372_DL_3GB 0x8A +#define XST352_BYTE1_ST372_2x720L_3GB 0x8B +#define XST352_BYTE1_ST372_2x1080L_3GB 0x8C +#define XST352_BYTE1_ST2081_10_2160L_6G 0xC0 +#define XST352_BYTE1_ST2081_10_DL_2160L_6G 0xC2 +#define XST352_BYTE1_ST2082_10_2160L_12G 0xCE + +#define XST352_BYTE2_TS_TYPE_MASK BIT(15) +#define XST352_BYTE2_TS_TYPE_OFFSET 15 +#define XST352_BYTE2_PIC_TYPE_MASK BIT(14) +#define XST352_BYTE2_PIC_TYPE_OFFSET 14 +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED 0 +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE 1 + +#define XST352_BYTE2_FPS_MASK 0xF +#define XST352_BYTE2_FPS_SHIFT 8 +#define XST352_BYTE2_FPS_24F 0x2 +#define XST352_BYTE2_FPS_24 0x3 +#define XST352_BYTE2_FPS_48F 0x4 +#define XST352_BYTE2_FPS_25 0x5 +#define XST352_BYTE2_FPS_30F 0x6 +#define XST352_BYTE2_FPS_30 0x7 +#define XST352_BYTE2_FPS_48 0x8 +#define XST352_BYTE2_FPS_50 0x9 +#define XST352_BYTE2_FPS_60F 0xA +#define XST352_BYTE2_FPS_60 0xB +/* Table 4 ST 2081-10:2015 */ +#define XST352_BYTE2_FPS_96 0xC +#define XST352_BYTE2_FPS_100 0xD +#define XST352_BYTE2_FPS_120 0xE +#define XST352_BYTE2_FPS_120F 0xF + +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK BIT(22) +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET 22 + +#define XST352_BYTE3_COLOR_FORMAT_MASK GENMASK(19, 16) +#define XST352_BYTE3_COLOR_FORMAT_OFFSET 16 +#define XST352_BYTE3_COLOR_FORMAT_422 0x0 +#define XST352_BYTE3_COLOR_FORMAT_420 0x3 + +/** + * enum sdi_family_enc - SDI Transport Video Format Detected with Active Pixels + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080 + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720 + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP 2048x1080 + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080 + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486 + * @XSDIRX_PAL: PAL encoding detected with AP 720x576 + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type + */ +enum sdi_family_enc { + XSDIRX_SMPTE_ST_274 = 0, + XSDIRX_SMPTE_ST_296 = 1, + XSDIRX_SMPTE_ST_2048_2 = 2, + XSDIRX_SMPTE_ST_295 = 3, + XSDIRX_NTSC = 8, + XSDIRX_PAL = 9, + XSDIRX_TS_UNKNOWN = 15 +}; + +/** + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device structure + * @dev: Platform structure + * @iomem: Base address of subsystem + * @irq: requested irq number + * @include_edh: EDH processor presence + * @mode: 3G/6G/12G mode + * @axi_clk: Axi lite interface clock + * @sdirx_clk: SDI Rx GT clock + * @vidout_clk: Video clock + */ +struct xsdirxss_core { + struct device *dev; + void __iomem *iomem; + int irq; + bool include_edh; + int mode; + struct clk *axi_clk; + struct clk *sdirx_clk; + struct clk *vidout_clk; +}; + +/** + * struct xsdirxss_state - SDI Rx Subsystem device structure + * @core: Core structure for MIPI SDI Rx Subsystem + * @subdev: The v4l2 subdev structure + * @ctrl_handler: control handler + * @event: Holds the video unlock event + * @formats: Active V4L2 formats on each pad + * @default_format: default V4L2 media bus format + * @frame_interval: Captures the frame rate + * @vip_format: format information corresponding to the active format + * @pads: media pads + * @streaming: Flag for storing streaming state + * @vidlocked: Flag indicating SDI Rx has locked onto video stream + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced. + * + * This structure contains the device driver related parameters + */ +struct xsdirxss_state { + struct xsdirxss_core core; + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_event event; + struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS]; + struct v4l2_mbus_framefmt default_format; + struct v4l2_fract frame_interval; + const struct xvip_video_format *vip_format; + struct media_pad pads[XSDIRX_MEDIA_PADS]; + bool streaming; + bool vidlocked; + bool ts_is_interlaced; +}; + +static inline struct xsdirxss_state * +to_xsdirxssstate(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xsdirxss_state, subdev); +} + +/* + * Register related operations + */ +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr) +{ + return ioread32(xsdirxss->iomem + addr); +} + +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr, + u32 value) +{ + iowrite32(value, xsdirxss->iomem + addr); +} + +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr, + u32 clr) +{ + xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr); +} + +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr, + u32 set) +{ + xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set); +} + +static void xsdirx_core_disable(struct xsdirxss_core *core) +{ + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK); +} + +static void xsdirx_core_enable(struct xsdirxss_core *core) +{ + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK); +} + +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask) +{ + u32 i, val; + + mask &= XSDIRX_DETECT_ALL_MODES; + if (!mask) { + dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask); + return -EINVAL; + } + + dev_dbg(core->dev, "mask = 0x%x\n", mask); + + val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG); + val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK); + val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK); + val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK); + + if (hweight16(mask) > 1) { + /* Multi mode detection as more than 1 bit set in mask */ + dev_dbg(core->dev, "Detect multiple modes\n"); + for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) { + switch (mask & (1 << i)) { + case BIT(XSDIRX_MODE_SD_OFFSET): + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK; + break; + case BIT(XSDIRX_MODE_HD_OFFSET): + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK; + break; + case BIT(XSDIRX_MODE_3G_OFFSET): + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK; + break; + case BIT(XSDIRX_MODE_6G_OFFSET): + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK; + break; + case BIT(XSDIRX_MODE_12GI_OFFSET): + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK; + break; + case BIT(XSDIRX_MODE_12GF_OFFSET): + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK; + break; + } + } + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK; + } else { + /* Fixed Mode */ + u32 forced_mode_mask = 0; + + dev_dbg(core->dev, "Detect fixed mode\n"); + + /* Find offset of first bit set */ + switch (__ffs(mask)) { + case XSDIRX_MODE_SD_OFFSET: + forced_mode_mask = XSDIRX_MODE_SD_MASK; + break; + case XSDIRX_MODE_HD_OFFSET: + forced_mode_mask = XSDIRX_MODE_HD_MASK; + break; + case XSDIRX_MODE_3G_OFFSET: + forced_mode_mask = XSDIRX_MODE_3G_MASK; + break; + case XSDIRX_MODE_6G_OFFSET: + forced_mode_mask = XSDIRX_MODE_6G_MASK; + break; + case XSDIRX_MODE_12GI_OFFSET: + forced_mode_mask = XSDIRX_MODE_12GI_MASK; + break; + case XSDIRX_MODE_12GF_OFFSET: + forced_mode_mask = XSDIRX_MODE_12GF_MASK; + break; + } + dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n", + forced_mode_mask); + val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET; + } + + dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n", + val); + xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val); + + return 0; +} + +static void xsdirx_framer(struct xsdirxss_core *core, bool flag) +{ + if (flag) + xsdirxss_set(core, XSDIRX_MDL_CTRL_REG, + XSDIRX_MDL_CTRL_FRM_EN_MASK); + else + xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG, + XSDIRX_MDL_CTRL_FRM_EN_MASK); +} + +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable) +{ + u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG); + + val = enable & XSDIRX_EDH_ALLERR_MASK; + + xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val); +} + +static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val) +{ + /* + * The video lock window is the amount of time for which the + * the mode and transport stream should be locked to get the + * video lock interrupt. + */ + xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val); +} + +static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask) +{ + xsdirxss_clr(core, XSDIRX_IER_REG, mask); +} + +static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask) +{ + xsdirxss_set(core, XSDIRX_IER_REG, mask); +} + +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag) +{ + if (flag) + xsdirxss_set(core, XSDIRX_GLBL_IER_REG, + XSDIRX_GLBL_INTR_EN_MASK); + else + xsdirxss_clr(core, XSDIRX_GLBL_IER_REG, + XSDIRX_GLBL_INTR_EN_MASK); +} + +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask) +{ + xsdirxss_set(core, XSDIRX_ISR_REG, mask); +} + +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool enable) +{ + if (enable) + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, + XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK); + else + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, + XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK); +} + +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool enable) +{ + if (enable) + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, + XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK); + else + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, + XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK); +} + +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool enable) +{ + /* The sdi to native bridge is followed by native to axis4 bridge */ + if (enable) { + xsdirx_axis4_bridge_control(core, enable); + xsdirx_vid_bridge_control(core, enable); + } else { + xsdirx_vid_bridge_control(core, enable); + xsdirx_axis4_bridge_control(core, enable); + } +} + +static void xsdirx_streamdowncb(struct xsdirxss_core *core) +{ + xsdirx_streamflow_control(core, false); +} + +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval, + u32 framerate) +{ + switch (framerate) { + case XSDIRX_TS_DET_STAT_RATE_23_98HZ: + frame_interval->numerator = 1001; + frame_interval->denominator = 24000; + break; + case XSDIRX_TS_DET_STAT_RATE_24HZ: + frame_interval->numerator = 1000; + frame_interval->denominator = 24000; + break; + case XSDIRX_TS_DET_STAT_RATE_25HZ: + frame_interval->numerator = 1000; + frame_interval->denominator = 25000; + break; + case XSDIRX_TS_DET_STAT_RATE_29_97HZ: + frame_interval->numerator = 1001; + frame_interval->denominator = 30000; + break; + case XSDIRX_TS_DET_STAT_RATE_30HZ: + frame_interval->numerator = 1000; + frame_interval->denominator = 30000; + break; + case XSDIRX_TS_DET_STAT_RATE_47_95HZ: + frame_interval->numerator = 1001; + frame_interval->denominator = 48000; + break; + case XSDIRX_TS_DET_STAT_RATE_48HZ: + frame_interval->numerator = 1000; + frame_interval->denominator = 48000; + break; + case XSDIRX_TS_DET_STAT_RATE_50HZ: + frame_interval->numerator = 1000; + frame_interval->denominator = 50000; + break; + case XSDIRX_TS_DET_STAT_RATE_59_94HZ: + frame_interval->numerator = 1001; + frame_interval->denominator = 60000; + break; + case XSDIRX_TS_DET_STAT_RATE_60HZ: + frame_interval->numerator = 1000; + frame_interval->denominator = 60000; + break; + default: + frame_interval->numerator = 1; + frame_interval->denominator = 1; + } +} + +/** + * xsdirx_get_stream_properties - Get SDI Rx stream properties + * @state: pointer to driver state + * + * This function decodes the stream's ST352 payload (if available) to get + * stream properties like width, height, picture type (interlaced/progressive), + * etc. + * + * Return: 0 for success else errors + */ +static int xsdirx_get_stream_properties(struct xsdirxss_state *state) +{ + struct xsdirxss_core *core = &state->core; + u32 mode, payload = 0, val, family, valid, tscan; + u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0; + u8 sampling = XST352_BYTE3_COLOR_FORMAT_422; + struct v4l2_mbus_framefmt *format = &state->formats[0]; + + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG); + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK; + + valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG); + + if ((mode >= XSDIRX_MODE_3G_MASK) && !valid) { + dev_err(core->dev, "No valid ST352 payload present even for 3G mode and above\n"); + return -EINVAL; + } + + val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG); + if (valid & XSDIRX_ST352_VALID_DS1_MASK) { + payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG); + byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) & + XST352_PAYLOAD_BYTE_MASK; + active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >> + XST352_BYTE3_ACT_LUMA_COUNT_OFFSET; + pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >> + XST352_BYTE2_PIC_TYPE_OFFSET; + framerate = (payload >> XST352_BYTE2_FPS_SHIFT) & + XST352_BYTE2_FPS_MASK; + tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >> + XST352_BYTE2_TS_TYPE_OFFSET; + sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >> + XST352_BYTE3_COLOR_FORMAT_OFFSET; + } else { + dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n", + mode); + framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >> + XSDIRX_TS_DET_STAT_RATE_OFFSET; + tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >> + XSDIRX_TS_DET_STAT_SCAN_OFFSET; + } + + family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >> + XSDIRX_TS_DET_STAT_FAMILY_OFFSET; + state->ts_is_interlaced = tscan ? false : true; + + dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n", + state->ts_is_interlaced, family); + + switch (mode) { + case XSDIRX_MODE_HD_MASK: + if (!valid) { + /* No payload obtained */ + dev_dbg(core->dev, "frame rate : %d, tscan = %d\n", + framerate, tscan); + /* + * NOTE : A progressive segmented frame pSF will be + * reported incorrectly as Interlaced as we rely on IP's + * transport scan locked bit. + */ + dev_warn(core->dev, "pSF will be incorrectly reported as Interlaced\n"); + + switch (framerate) { + case XSDIRX_TS_DET_STAT_RATE_23_98HZ: + case XSDIRX_TS_DET_STAT_RATE_24HZ: + case XSDIRX_TS_DET_STAT_RATE_25HZ: + case XSDIRX_TS_DET_STAT_RATE_29_97HZ: + case XSDIRX_TS_DET_STAT_RATE_30HZ: + if (family == XSDIRX_SMPTE_ST_296) { + format->width = 1280; + format->height = 720; + format->field = V4L2_FIELD_NONE; + } else if (family == XSDIRX_SMPTE_ST_2048_2) { + format->width = 2048; + format->height = 1080; + if (tscan) + format->field = V4L2_FIELD_NONE; + else + format->field = + V4L2_FIELD_ALTERNATE; + } else { + format->width = 1920; + format->height = 1080; + if (tscan) + format->field = V4L2_FIELD_NONE; + else + format->field = + V4L2_FIELD_ALTERNATE; + } + break; + case XSDIRX_TS_DET_STAT_RATE_50HZ: + case XSDIRX_TS_DET_STAT_RATE_59_94HZ: + case XSDIRX_TS_DET_STAT_RATE_60HZ: + if (family == XSDIRX_SMPTE_ST_274) { + format->width = 1920; + format->height = 1080; + } else { + format->width = 1280; + format->height = 720; + } + format->field = V4L2_FIELD_NONE; + break; + default: + format->width = 1920; + format->height = 1080; + format->field = V4L2_FIELD_NONE; + } + } else { + dev_dbg(core->dev, "Got the payload\n"); + switch (byte1) { + case XST352_BYTE1_ST292_1x720L_1_5G: + /* SMPTE ST 292-1 for 720 line payloads */ + format->width = 1280; + format->height = 720; + break; + case XST352_BYTE1_ST292_1x1080L_1_5G: + /* SMPTE ST 292-1 for 1080 line payloads */ + format->height = 1080; + if (active_luma) + format->width = 2048; + else + format->width = 1920; + break; + default: + dev_dbg(core->dev, "Unknown HD Mode SMPTE standard\n"); + return -EINVAL; + } + } + break; + case XSDIRX_MODE_SD_MASK: + format->field = V4L2_FIELD_ALTERNATE; + + switch (family) { + case XSDIRX_NTSC: + format->width = 720; + format->height = 486; + break; + case XSDIRX_PAL: + format->width = 720; + format->height = 576; + break; + default: + dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n"); + return -EINVAL; + } + break; + case XSDIRX_MODE_3G_MASK: + switch (byte1) { + case XST352_BYTE1_ST425_2008_750L_3GB: + /* Sec 4.1.6.1 SMPTE 425-2008 */ + case XST352_BYTE1_ST372_2x720L_3GB: + /* Table 13 SMPTE 425-2008 */ + format->width = 1280; + format->height = 720; + break; + case XST352_BYTE1_ST425_2008_1125L_3GA: + /* ST352 Table SMPTE 425-1 */ + case XST352_BYTE1_ST372_DL_3GB: + /* Table 13 SMPTE 425-2008 */ + case XST352_BYTE1_ST372_2x1080L_3GB: + /* Table 13 SMPTE 425-2008 */ + format->height = 1080; + if (active_luma) + format->width = 2048; + else + format->width = 1920; + break; + default: + dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n"); + return -EINVAL; + } + break; + case XSDIRX_MODE_6G_MASK: + switch (byte1) { + case XST352_BYTE1_ST2081_10_DL_2160L_6G: + /* Dual link 6G */ + case XST352_BYTE1_ST2081_10_2160L_6G: + /* Table 3 SMPTE ST 2081-10 */ + format->height = 2160; + if (active_luma) + format->width = 4096; + else + format->width = 3840; + break; + default: + dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n"); + return -EINVAL; + } + break; + case XSDIRX_MODE_12GI_MASK: + case XSDIRX_MODE_12GF_MASK: + switch (byte1) { + case XST352_BYTE1_ST2082_10_2160L_12G: + /* Section 4.3.1 SMPTE ST 2082-10 */ + format->height = 2160; + if (active_luma) + format->width = 4096; + else + format->width = 3840; + break; + default: + dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n"); + return -EINVAL; + } + break; + default: + dev_err(core->dev, "Invalid Mode\n"); + return -EINVAL; + } + + if (valid) { + if (pic_type) + format->field = V4L2_FIELD_NONE; + else + format->field = V4L2_FIELD_ALTERNATE; + } + + if (format->field == V4L2_FIELD_ALTERNATE) + format->height = format->height / 2; + + switch (sampling) { + case XST352_BYTE3_COLOR_FORMAT_420: + format->code = MEDIA_BUS_FMT_VYYUYY10_4X20; + break; + case XST352_BYTE3_COLOR_FORMAT_422: + format->code = MEDIA_BUS_FMT_UYVY10_1X20; + break; + default: + dev_err(core->dev, "Unsupported color format : %d\n", sampling); + return -EINVAL; + } + + xsdirxss_get_framerate(&state->frame_interval, framerate); + + dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload = 0x%08x ts = 0x%08x\n", + format->width, format->height, format->field, payload, val); + dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n", + state->frame_interval.numerator, + state->frame_interval.denominator); + dev_dbg(core->dev, "Stream code = 0x%x\n", format->code); + return 0; +} + +/** + * xsdirxss_irq_handler - Interrupt handler for SDI Rx + * @irq: IRQ number + * @dev_id: Pointer to device state + * + * The SDI Rx interrupts are cleared by first setting and then clearing the bits + * in the interrupt clear register. The interrupt status register is read only. + * + * Return: IRQ_HANDLED after handling interrupts + */ +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id) +{ + struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id; + struct xsdirxss_core *core = &state->core; + u32 status; + + status = xsdirxss_read(core, XSDIRX_ISR_REG); + dev_dbg(core->dev, "interrupt status = 0x%08x\n", status); + + if (!status) + return IRQ_NONE; + + if (status & XSDIRX_INTR_VIDLOCK_MASK) { + u32 val1, val2; + + dev_dbg(core->dev, "video lock interrupt\n"); + xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK); + + val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG); + val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG); + + if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) && + (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) { + u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK | + XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK; + + dev_dbg(core->dev, "mode & ts lock occurred\n"); + + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask); + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask); + + val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG); + val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG); + + dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1); + dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2); + + if (!xsdirx_get_stream_properties(state)) { + memset(&state->event, 0, sizeof(state->event)); + state->event.type = V4L2_EVENT_SOURCE_CHANGE; + state->event.u.src_change.changes = + V4L2_EVENT_SRC_CH_RESOLUTION; + v4l2_subdev_notify_event(&state->subdev, + &state->event); + + state->vidlocked = true; + } else { + dev_err(core->dev, "Unable to get stream properties!\n"); + state->vidlocked = false; + } + } else { + dev_dbg(core->dev, "video unlock before video lock!\n"); + state->vidlocked = false; + } + } + + if (status & XSDIRX_INTR_VIDUNLOCK_MASK) { + dev_dbg(core->dev, "video unlock interrupt\n"); + xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK); + xsdirx_streamdowncb(core); + + memset(&state->event, 0, sizeof(state->event)); + state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK; + v4l2_subdev_notify_event(&state->subdev, &state->event); + + state->vidlocked = false; + } + + if (status & XSDIRX_INTR_UNDERFLOW_MASK) { + dev_dbg(core->dev, "Video in to AXI4 Stream core underflow interrupt\n"); + xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK); + + memset(&state->event, 0, sizeof(state->event)); + state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW; + v4l2_subdev_notify_event(&state->subdev, &state->event); + } + + if (status & XSDIRX_INTR_OVERFLOW_MASK) { + dev_dbg(core->dev, "Video in to AXI4 Stream core overflow interrupt\n"); + xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK); + + memset(&state->event, 0, sizeof(state->event)); + state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW; + v4l2_subdev_notify_event(&state->subdev, &state->event); + } + return IRQ_HANDLED; +} + +/** + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event + * @sd: V4L2 Sub device + * @fh: V4L2 File Handle + * @sub: Subcribe event structure + * + * Return: 0 on success, errors otherwise + */ +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + int ret; + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd); + struct xsdirxss_core *core = &xsdirxss->core; + + switch (sub->type) { + case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK: + case V4L2_EVENT_XLNXSDIRX_UNDERFLOW: + case V4L2_EVENT_XLNXSDIRX_OVERFLOW: + ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL); + break; + case V4L2_EVENT_SOURCE_CHANGE: + ret = v4l2_src_change_event_subscribe(fh, sub); + break; + default: + return -EINVAL; + } + dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type); + return ret; +} + +/** + * xsdirxss_unsubscribe_event - Unsubscribe from all events registered + * @sd: V4L2 Sub device + * @fh: V4L2 file handle + * @sub: pointer to Event unsubscription structure + * + * Return: zero on success, else a negative error code. + */ +static int xsdirxss_unsubscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd); + struct xsdirxss_core *core = &xsdirxss->core; + + dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type); + return v4l2_event_unsubscribe(fh, sub); +} + +/** + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls + * @ctrl: V4L2 control to be set + * + * This function is used to set the V4L2 controls for the Xilinx SDI Rx + * Subsystem. + * + * Return: 0 on success, errors otherwise + */ +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl) +{ + int ret = 0; + struct xsdirxss_state *xsdirxss = + container_of(ctrl->handler, + struct xsdirxss_state, ctrl_handler); + struct xsdirxss_core *core = &xsdirxss->core; + + dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n", + ctrl->id, ctrl->val); + + if (xsdirxss->streaming) { + dev_err(core->dev, "Cannot set controls while streaming\n"); + return -EINVAL; + } + + xsdirx_core_disable(core); + switch (ctrl->id) { + case V4L2_CID_XILINX_SDIRX_FRAMER: + xsdirx_framer(core, ctrl->val); + break; + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW: + xsdirx_setvidlockwindow(core, ctrl->val); + break; + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE: + xsdirx_setedherrcnttrigger(core, ctrl->val); + break; + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES: + if (ctrl->val) { + if (core->mode == XSDIRXSS_SDI_STD_3G) { + dev_dbg(core->dev, "Upto 3G supported\n"); + ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) | + BIT(XSDIRX_MODE_12GI_OFFSET) | + BIT(XSDIRX_MODE_12GF_OFFSET)); + } + + if (core->mode == XSDIRXSS_SDI_STD_6G) { + dev_dbg(core->dev, "Upto 6G supported\n"); + ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) | + BIT(XSDIRX_MODE_12GF_OFFSET)); + } + + ret = xsdirx_set_modedetect(core, ctrl->val); + } else { + dev_err(core->dev, "Select at least one mode!\n"); + return -EINVAL; + } + break; + default: + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, + XSDIRX_RST_CTRL_SS_EN_MASK); + return -EINVAL; + } + xsdirx_core_enable(core); + return ret; +} + +/** + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls + * @ctrl: Pointer to V4L2 control + * + * Return: 0 on success, errors otherwise + */ +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + u32 val; + struct xsdirxss_state *xsdirxss = + container_of(ctrl->handler, + struct xsdirxss_state, ctrl_handler); + struct xsdirxss_core *core = &xsdirxss->core; + + switch (ctrl->id) { + case V4L2_CID_XILINX_SDIRX_MODE_DETECT: + if (!xsdirxss->vidlocked) { + dev_err(core->dev, "Can't get values when video not locked!\n"); + return -EINVAL; + } + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG); + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK; + + switch (val) { + case XSDIRX_MODE_SD_MASK: + ctrl->val = XSDIRX_MODE_SD_OFFSET; + break; + case XSDIRX_MODE_HD_MASK: + ctrl->val = XSDIRX_MODE_HD_OFFSET; + break; + case XSDIRX_MODE_3G_MASK: + ctrl->val = XSDIRX_MODE_3G_OFFSET; + break; + case XSDIRX_MODE_6G_MASK: + ctrl->val = XSDIRX_MODE_6G_OFFSET; + break; + case XSDIRX_MODE_12GI_MASK: + ctrl->val = XSDIRX_MODE_12GI_OFFSET; + break; + case XSDIRX_MODE_12GF_MASK: + ctrl->val = XSDIRX_MODE_12GF_OFFSET; + break; + } + break; + case V4L2_CID_XILINX_SDIRX_CRC: + ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG); + xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF); + break; + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT: + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG); + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK; + if (val == XSDIRX_MODE_SD_MASK) { + ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG); + } else { + dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id); + return -EINVAL; + } + break; + case V4L2_CID_XILINX_SDIRX_EDH_STATUS: + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG); + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK; + if (val == XSDIRX_MODE_SD_MASK) { + ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG); + } else { + dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id); + return -EINVAL; + } + break; + case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED: + if (!xsdirxss->vidlocked) { + dev_err(core->dev, "Can't get values when video not locked!\n"); + return -EINVAL; + } + ctrl->val = xsdirxss->ts_is_interlaced; + break; + case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS: + if (!xsdirxss->vidlocked) { + dev_err(core->dev, "Can't get values when video not locked!\n"); + return -EINVAL; + } + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG); + val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK; + val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET; + ctrl->val = 1 << val; + break; + case V4L2_CID_XILINX_SDIRX_IS_3GB: + if (!xsdirxss->vidlocked) { + dev_err(core->dev, "Can't get values when video not locked!\n"); + return -EINVAL; + } + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG); + val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK; + ctrl->val = val ? true : false; + break; + default: + dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id); + return -EINVAL; + } + dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n", + ctrl->id, ctrl->val); + return 0; +} + +/** + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem + * @sd: Pointer to V4L2 subdevice structure + * + * This function prints the current status of Xilinx SDI Rx Subsystem + * + * Return: 0 on success + */ +static int xsdirxss_log_status(struct v4l2_subdev *sd) +{ + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd); + struct xsdirxss_core *core = &xsdirxss->core; + u32 data, i; + + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n"); + for (i = 0; i < 0x28; i++) { + data = xsdirxss_read(core, i * 4); + v4l2_info(sd, "offset 0x%08x data 0x%08x\n", + i * 4, data); + } + v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n"); + return 0; +} + +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss) +{ + xsdirx_streamflow_control(&xsdirxss->core, true); +} + +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss) +{ + xsdirx_streamflow_control(&xsdirxss->core, false); +} + +/** + * xsdirxss_g_frame_interval - Get the frame interval + * @sd: V4L2 Sub device + * @fi: Pointer to V4l2 Sub device frame interval structure + * + * This function is used to get the frame interval. + * The frame rate can be integral or fractional. + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24 fps + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 => 23.97 fps + * + * Return: 0 on success + */ +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd); + struct xsdirxss_core *core = &xsdirxss->core; + + if (!xsdirxss->vidlocked) { + dev_err(core->dev, "Video not locked!\n"); + return -EINVAL; + } + + fi->interval = xsdirxss->frame_interval; + + dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n", + xsdirxss->frame_interval.numerator, + xsdirxss->frame_interval.denominator); + return 0; +} + +/** + * xsdirxss_s_stream - It is used to start/stop the streaming. + * @sd: V4L2 Sub device + * @enable: Flag (True / False) + * + * This function controls the start or stop of streaming for the + * Xilinx SDI Rx Subsystem. + * + * Return: 0 on success, errors otherwise + */ +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd); + struct xsdirxss_core *core = &xsdirxss->core; + + if (enable) { + if (!xsdirxss->vidlocked) { + dev_dbg(core->dev, "Video is not locked\n"); + return -EINVAL; + } + if (xsdirxss->streaming) { + dev_dbg(core->dev, "Already streaming\n"); + return -EINVAL; + } + + xsdirxss_start_stream(xsdirxss); + xsdirxss->streaming = true; + dev_dbg(core->dev, "Streaming started\n"); + } else { + if (!xsdirxss->streaming) { + dev_dbg(core->dev, "Stopped streaming already\n"); + return -EINVAL; + } + + xsdirxss_stop_stream(xsdirxss); + xsdirxss->streaming = false; + dev_dbg(core->dev, "Streaming stopped\n"); + } + + return 0; +} + +static struct v4l2_mbus_framefmt * +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xsdirxss->formats[pad]; + default: + return NULL; + } +} + +/** + * xsdirxss_get_format - Get the pad format + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * @fmt: Pointer to pad level media bus format + * + * This function is used to get the pad format information. + * + * Return: 0 on success + */ +static int xsdirxss_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd); + struct xsdirxss_core *core = &xsdirxss->core; + + if (!xsdirxss->vidlocked) { + dev_err(core->dev, "Video not locked!\n"); + return -EINVAL; + } + + fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg, + fmt->pad, fmt->which); + + dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n", + fmt->format.width, fmt->format.height, fmt->format.field); + + return 0; +} + +/** + * xsdirxss_set_format - This is used to set the pad format + * @sd: Pointer to V4L2 Sub device structure + * @cfg: Pointer to sub device pad information structure + * @fmt: Pointer to pad level media bus format + * + * This function is used to set the pad format. + * Since the pad format is fixed in hardware, it can't be + * modified on run time. + * + * Return: 0 on success + */ +static int xsdirxss_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *__format; + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd); + + dev_dbg(xsdirxss->core.dev, + "set width %d height %d code %d field %d colorspace %d\n", + fmt->format.width, fmt->format.height, + fmt->format.code, fmt->format.field, + fmt->format.colorspace); + + __format = __xsdirxss_get_pad_format(xsdirxss, cfg, + fmt->pad, fmt->which); + + /* Currently reset the code to one fixed in hardware */ + /* TODO : Add checks for width height */ + fmt->format.code = __format->code; + + return 0; +} + +/** + * xsdirxss_open - Called on v4l2_open() + * @sd: Pointer to V4L2 sub device structure + * @fh: Pointer to V4L2 File handle + * + * This function is called on v4l2_open(). It sets the default format for pad. + * + * Return: 0 on success + */ +static int xsdirxss_open(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format; + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd); + + format = v4l2_subdev_get_try_format(sd, fh->pad, 0); + *format = xsdirxss->default_format; + + return 0; +} + +/** + * xsdirxss_close - Called on v4l2_close() + * @sd: Pointer to V4L2 sub device structure + * @fh: Pointer to V4L2 File handle + * + * This function is called on v4l2_close(). + * + * Return: 0 on success + */ +static int xsdirxss_close(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + return 0; +} + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static const struct media_entity_operations xsdirxss_media_ops = { + .link_validate = v4l2_subdev_link_validate +}; + +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = { + .g_volatile_ctrl = xsdirxss_g_volatile_ctrl, + .s_ctrl = xsdirxss_s_ctrl +}; + +static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = { + { + .ops = &xsdirxss_ctrl_ops, + .id = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE, + .name = "SDI Rx : EDH Error Count Enable", + .type = V4L2_CTRL_TYPE_BITMASK, + .min = 0, + .max = XSDIRX_EDH_ALLERR_MASK, + .def = 0, + }, { + .ops = &xsdirxss_ctrl_ops, + .id = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT, + .name = "SDI Rx : EDH Error Count", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 0xFFFF, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, { + .ops = &xsdirxss_ctrl_ops, + .id = V4L2_CID_XILINX_SDIRX_EDH_STATUS, + .name = "SDI Rx : EDH Status", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 0xFFFFFFFF, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + } +}; + +static struct v4l2_ctrl_config xsdirxss_ctrls[] = { + { + .ops = &xsdirxss_ctrl_ops, + .id = V4L2_CID_XILINX_SDIRX_FRAMER, + .name = "SDI Rx : Enable Framer", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = true, + }, { + .ops = &xsdirxss_ctrl_ops, + .id = V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW, + .name = "SDI Rx : Video Lock Window", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 0xFFFFFFFF, + .step = 1, + .def = XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW, + }, { + .ops = &xsdirxss_ctrl_ops, + .id = V4L2_CID_XILINX_SDIRX_SEARCH_MODES, + .name = "SDI Rx : Modes search Mask", + .type = V4L2_CTRL_TYPE_BITMASK, + .min = 0, + .max = XSDIRX_DETECT_ALL_MODES, + .def = XSDIRX_DETECT_ALL_MODES, + }, { + .ops = &xsdirxss_ctrl_ops, + .id = V4L2_CID_XILINX_SDIRX_MODE_DETECT, + .name = "SDI Rx : Mode Detect Status", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = XSDIRX_MODE_SD_OFFSET, + .max = XSDIRX_MODE_12GF_OFFSET, + .step = 1, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, { + .ops = &xsdirxss_ctrl_ops, + .id = V4L2_CID_XILINX_SDIRX_CRC, + .name = "SDI Rx : CRC Error status", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 0xFFFFFFFF, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, { + .ops = &xsdirxss_ctrl_ops, + .id = V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED, + .name = "SDI Rx : TS is Interlaced", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .def = false, + .step = 1, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, { + .ops = &xsdirxss_ctrl_ops, + .id = V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS, + .name = "SDI Rx : Active Streams", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 1, + .max = 16, + .def = 1, + .step = 1, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, { + .ops = &xsdirxss_ctrl_ops, + .id = V4L2_CID_XILINX_SDIRX_IS_3GB, + .name = "SDI Rx : Is 3GB", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .def = false, + .step = 1, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + } +}; + +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = { + .log_status = xsdirxss_log_status, + .subscribe_event = xsdirxss_subscribe_event, + .unsubscribe_event = xsdirxss_unsubscribe_event +}; + +static struct v4l2_subdev_video_ops xsdirxss_video_ops = { + .g_frame_interval = xsdirxss_g_frame_interval, + .s_stream = xsdirxss_s_stream +}; + +static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = { + .get_fmt = xsdirxss_get_format, + .set_fmt = xsdirxss_set_format, +}; + +static struct v4l2_subdev_ops xsdirxss_ops = { + .core = &xsdirxss_core_ops, + .video = &xsdirxss_video_ops, + .pad = &xsdirxss_pad_ops +}; + +static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = { + .open = xsdirxss_open, + .close = xsdirxss_close +}; + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss) +{ + struct device_node *node = xsdirxss->core.dev->of_node; + struct device_node *ports = NULL; + struct device_node *port = NULL; + unsigned int nports = 0; + struct xsdirxss_core *core = &xsdirxss->core; + int ret; + const char *sdi_std; + + core->include_edh = of_property_read_bool(node, "xlnx,include-edh"); + dev_dbg(core->dev, "EDH property = %s\n", + core->include_edh ? "Present" : "Absent"); + + ret = of_property_read_string(node, "xlnx,line-rate", + &sdi_std); + if (ret < 0) { + dev_err(core->dev, "xlnx,line-rate property not found\n"); + return ret; + } + + if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) { + core->mode = XSDIRXSS_SDI_STD_12G_8DS; + } else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) { + core->mode = XSDIRXSS_SDI_STD_6G; + } else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) { + core->mode = XSDIRXSS_SDI_STD_3G; + } else { + dev_err(core->dev, "Invalid Line Rate\n"); + return -EINVAL; + } + dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std, + core->mode); + + ports = of_get_child_by_name(node, "ports"); + if (!ports) + ports = node; + + for_each_child_of_node(ports, port) { + const struct xvip_video_format *format; + struct device_node *endpoint; + + if (!port->name || of_node_cmp(port->name, "port")) + continue; + + format = xvip_of_get_format(port); + if (IS_ERR(format)) { + dev_err(core->dev, "invalid format in DT"); + return PTR_ERR(format); + } + + dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n", + format->vf_code, format->width, format->bpp); + + if (format->vf_code != XVIP_VF_YUV_422 && + format->vf_code != XVIP_VF_YUV_420) { + dev_err(core->dev, "Incorrect UG934 video format set.\n"); + return -EINVAL; + } + xsdirxss->vip_format = format; + + endpoint = of_get_next_child(port, NULL); + if (!endpoint) { + dev_err(core->dev, "No port at\n"); + return -EINVAL; + } + + /* Count the number of ports. */ + nports++; + } + + if (nports != 1) { + dev_err(core->dev, "invalid number of ports %u\n", nports); + return -EINVAL; + } + + /* Register interrupt handler */ + core->irq = irq_of_parse_and_map(node, 0); + + ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler, + IRQF_SHARED, "xilinx-sdirxss", xsdirxss); + if (ret) { + dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n", + ret); + return ret; + } + + return 0; +} + +static int xsdirxss_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct xsdirxss_state *xsdirxss; + struct xsdirxss_core *core; + struct resource *res; + int ret; + unsigned int num_ctrls, num_edh_ctrls = 0, i; + + xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL); + if (!xsdirxss) + return -ENOMEM; + + xsdirxss->core.dev = &pdev->dev; + core = &xsdirxss->core; + + core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk"); + if (IS_ERR(core->axi_clk)) { + ret = PTR_ERR(core->axi_clk); + dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret); + return ret; + } + + core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk"); + if (IS_ERR(core->sdirx_clk)) { + ret = PTR_ERR(core->sdirx_clk); + dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret); + return ret; + } + + core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk"); + if (IS_ERR(core->vidout_clk)) { + ret = PTR_ERR(core->vidout_clk); + dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret); + return ret; + } + + ret = clk_prepare_enable(core->axi_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret); + return ret; + } + + ret = clk_prepare_enable(core->sdirx_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret); + goto rx_clk_err; + } + + ret = clk_prepare_enable(core->vidout_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret); + goto vidout_clk_err; + } + + ret = xsdirxss_parse_of(xsdirxss); + if (ret < 0) + goto clk_err; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res); + if (IS_ERR(xsdirxss->core.iomem)) { + ret = PTR_ERR(xsdirxss->core.iomem); + goto clk_err; + } + + /* Reset the core */ + xsdirx_streamflow_control(core, false); + xsdirx_core_disable(core); + xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK); + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK); + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK); + xsdirx_globalintr(core, true); + xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF); + + /* Initialize V4L2 subdevice and media entity */ + xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE; + + /* Initialize the default format */ + xsdirxss->default_format.code = xsdirxss->vip_format->code; + xsdirxss->default_format.field = V4L2_FIELD_NONE; + xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT; + xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH; + xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT; + + xsdirxss->formats[0] = xsdirxss->default_format; + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xsdirxss->subdev; + v4l2_subdev_init(subdev, &xsdirxss_ops); + + subdev->dev = &pdev->dev; + subdev->internal_ops = &xsdirxss_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + + subdev->entity.ops = &xsdirxss_media_ops; + + v4l2_set_subdevdata(subdev, xsdirxss); + + ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads); + if (ret < 0) + goto error; + + /* Initialise and register the controls */ + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls); + + if (xsdirxss->core.include_edh) + num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls); + + v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler, + (num_ctrls + num_edh_ctrls)); + + for (i = 0; i < num_ctrls; i++) { + struct v4l2_ctrl *ctrl; + + dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n", + i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id); + + ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler, + &xsdirxss_ctrls[i], NULL); + if (!ctrl) { + dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n", + xsdirxss_ctrls[i].name); + goto error; + } + } + + if (xsdirxss->core.include_edh) { + for (i = 0; i < num_edh_ctrls; i++) { + struct v4l2_ctrl *ctrl; + + dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n", + i, xsdirxss_edh_ctrls[i].name, + xsdirxss_edh_ctrls[i].id); + + ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler, + &xsdirxss_edh_ctrls[i], + NULL); + if (!ctrl) { + dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n", + xsdirxss_edh_ctrls[i].name); + goto error; + } + } + } else { + dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as EDH is disabled in IP\n"); + } + + if (xsdirxss->ctrl_handler.error) { + dev_err(&pdev->dev, "failed to add controls\n"); + ret = xsdirxss->ctrl_handler.error; + goto error; + } + + subdev->ctrl_handler = &xsdirxss->ctrl_handler; + + ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler); + if (ret < 0) { + dev_err(&pdev->dev, "failed to set controls\n"); + goto error; + } + + platform_set_drvdata(pdev, xsdirxss); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + xsdirxss->streaming = false; + + dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n"); + + xsdirx_core_enable(core); + + return 0; +error: + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler); + media_entity_cleanup(&subdev->entity); + +clk_err: + clk_disable_unprepare(core->vidout_clk); +vidout_clk_err: + clk_disable_unprepare(core->sdirx_clk); +rx_clk_err: + clk_disable_unprepare(core->axi_clk); + return ret; +} + +static int xsdirxss_remove(struct platform_device *pdev) +{ + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xsdirxss->subdev; + + v4l2_async_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler); + media_entity_cleanup(&subdev->entity); + clk_disable_unprepare(xsdirxss->core.vidout_clk); + clk_disable_unprepare(xsdirxss->core.sdirx_clk); + clk_disable_unprepare(xsdirxss->core.axi_clk); + return 0; +} + +static const struct of_device_id xsdirxss_of_id_table[] = { + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" }, + { } +}; +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table); + +static struct platform_driver xsdirxss_driver = { + .driver = { + .name = "xilinx-sdirxss", + .of_match_table = xsdirxss_of_id_table, + }, + .probe = xsdirxss_probe, + .remove = xsdirxss_remove, +}; + +module_platform_driver(xsdirxss_driver); + +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>"); +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-switch.c b/drivers/media/platform/xilinx/xilinx-switch.c new file mode 100644 index 000000000000..b0052a76c65d --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-switch.c @@ -0,0 +1,460 @@ +/* + * Xilinx Video Switch + * + * Copyright (C) 2013-2015 Ideas on Board + * Copyright (C) 2013-2015 Xilinx, Inc. + * + * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <media/media-device.h> +#include <media/v4l2-async.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-vip.h" + +#define XSW_CORE_CH_CTRL 0x0100 +#define XSW_CORE_CH_CTRL_FORCE (1 << 3) + +#define XSW_SWITCH_STATUS 0x0104 + +/** + * struct xswitch_device - Xilinx Video Switch device structure + * @xvip: Xilinx Video IP device + * @pads: media pads + * @nsinks: number of sink pads (2 to 8) + * @nsources: number of source pads (1 to 8) + * @routing: sink pad connected to each source pad (-1 if none) + * @formats: active V4L2 media bus formats on sink pads + */ +struct xswitch_device { + struct xvip_device xvip; + + struct media_pad *pads; + unsigned int nsinks; + unsigned int nsources; + + int routing[8]; + + struct v4l2_mbus_framefmt *formats; +}; + +static inline struct xswitch_device *to_xsw(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xswitch_device, xvip.subdev); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Video Operations + */ + +static int xsw_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xswitch_device *xsw = to_xsw(subdev); + unsigned int unused_input; + unsigned int i; + u32 routing; + + if (!enable) { + xvip_stop(&xsw->xvip); + return 0; + } + + /* + * All outputs must be routed to an input. When less than 8 inputs are + * synthesized we can use input 7 for that purpose. Otherwise find an + * unused input to connect to unused outputs. + */ + if (xsw->nsinks == 8) { + u32 mask; + + for (i = 0, mask = 0xff; i < xsw->nsources; ++i) { + if (xsw->routing[i] != -1) + mask &= ~BIT(xsw->routing[i]); + } + + /* + * If all inputs are used all outputs are also used. We don't + * need an unused input in that case, use a zero value. + */ + unused_input = mask ? ffs(mask) - 1 : 0; + } else { + unused_input = 7; + } + + /* Configure routing. */ + for (i = 0, routing = 0; i < xsw->nsources; ++i) { + unsigned int route; + + route = xsw->routing[i] == -1 ? unused_input : xsw->routing[i]; + routing |= (XSW_CORE_CH_CTRL_FORCE | route) + << (i * 4); + } + + xvip_write(&xsw->xvip, XSW_CORE_CH_CTRL, routing); + + xvip_write(&xsw->xvip, XVIP_CTRL_CONTROL, + (((1 << xsw->nsources) - 1) << 4) | + XVIP_CTRL_CONTROL_SW_ENABLE); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static struct v4l2_mbus_framefmt * +xsw_get_pad_format(struct xswitch_device *xsw, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xsw->xvip.subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xsw->formats[pad]; + default: + return NULL; + } +} + +static int xsw_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xswitch_device *xsw = to_xsw(subdev); + int pad = fmt->pad; + + if (pad >= xsw->nsinks) { + pad = xsw->routing[pad - xsw->nsinks]; + if (pad < 0) { + memset(&fmt->format, 0, sizeof(fmt->format)); + return 0; + } + } + + fmt->format = *xsw_get_pad_format(xsw, cfg, pad, fmt->which); + + return 0; +} + +static int xsw_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xswitch_device *xsw = to_xsw(subdev); + struct v4l2_mbus_framefmt *format; + + /* The source pad format is always identical to the sink pad format and + * can't be modified. + */ + if (fmt->pad >= xsw->nsinks) + return xsw_get_format(subdev, cfg, fmt); + + format = xsw_get_pad_format(xsw, cfg, fmt->pad, fmt->which); + + format->code = fmt->format.code; + format->width = clamp_t(unsigned int, fmt->format.width, + XVIP_MIN_WIDTH, XVIP_MAX_WIDTH); + format->height = clamp_t(unsigned int, fmt->format.height, + XVIP_MIN_HEIGHT, XVIP_MAX_HEIGHT); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + return 0; +} + +static int xsw_get_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_routing *route) +{ + struct xswitch_device *xsw = to_xsw(subdev); + unsigned int i; + + mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex); + + for (i = 0; i < min(xsw->nsources, route->num_routes); ++i) { + route->routes[i].sink = xsw->routing[i]; + route->routes[i].source = i; + } + + route->num_routes = xsw->nsources; + + mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex); + + return 0; +} + +static int xsw_set_routing(struct v4l2_subdev *subdev, + struct v4l2_subdev_routing *route) +{ + struct xswitch_device *xsw = to_xsw(subdev); + unsigned int i; + int ret = 0; + + mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex); + + if (subdev->entity.stream_count) { + ret = -EBUSY; + goto done; + } + + for (i = 0; i < xsw->nsources; ++i) + xsw->routing[i] = -1; + + for (i = 0; i < route->num_routes; ++i) + xsw->routing[route->routes[i].source - xsw->nsinks] = + route->routes[i].sink; + +done: + mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex); + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +/** + * xsw_init_formats - Initialize formats on all pads + * @subdev: tpgper V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + * + * The function sets the format on pad 0 only. In two pads mode, this is the + * sink pad and the set format handler will propagate the format to the source + * pad. In one pad mode this is the source pad. + */ +static void xsw_init_formats(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + struct xswitch_device *xsw = to_xsw(subdev); + struct v4l2_subdev_format format; + unsigned int i; + + for (i = 0; i < xsw->nsinks; ++i) { + memset(&format, 0, sizeof(format)); + + format.pad = 0; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.width = 1920; + format.format.height = 1080; + + xsw_set_format(subdev, fh ? fh->pad : NULL, &format); + } +} + +static int xsw_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + xsw_init_formats(subdev, fh); + + return 0; +} + +static int xsw_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static struct v4l2_subdev_video_ops xsw_video_ops = { + .s_stream = xsw_s_stream, +}; + +static struct v4l2_subdev_pad_ops xsw_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xvip_enum_frame_size, + .get_fmt = xsw_get_format, + .set_fmt = xsw_set_format, + .get_routing = xsw_get_routing, + .set_routing = xsw_set_routing, +}; + +static struct v4l2_subdev_ops xsw_ops = { + .video = &xsw_video_ops, + .pad = &xsw_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xsw_internal_ops = { + .open = xsw_open, + .close = xsw_close, +}; + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static bool xsw_has_route(struct media_entity *entity, unsigned int pad0, + unsigned int pad1) +{ + struct xswitch_device *xsw = container_of(entity, struct xswitch_device, + xvip.subdev.entity); + unsigned int sink0, sink1; + + /* Two sinks are never connected together. */ + if (pad0 < xsw->nsinks && pad1 < xsw->nsinks) + return false; + + sink0 = pad0 < xsw->nsinks ? pad0 : xsw->routing[pad0 - xsw->nsinks]; + sink1 = pad1 < xsw->nsinks ? pad1 : xsw->routing[pad1 - xsw->nsinks]; + + return sink0 == sink1; +} + +static const struct media_entity_operations xsw_media_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_route = xsw_has_route, +}; + +/* ----------------------------------------------------------------------------- + * Platform Device Driver + */ + +static int xsw_parse_of(struct xswitch_device *xsw) +{ + struct device_node *node = xsw->xvip.dev->of_node; + int ret; + + ret = of_property_read_u32(node, "#xlnx,inputs", &xsw->nsinks); + if (ret < 0) { + dev_err(xsw->xvip.dev, "missing or invalid #xlnx,%s property\n", + "inputs"); + return ret; + } + + ret = of_property_read_u32(node, "#xlnx,outputs", &xsw->nsources); + if (ret < 0) { + dev_err(xsw->xvip.dev, "missing or invalid #xlnx,%s property\n", + "outputs"); + return ret; + } + + return 0; +} + +static int xsw_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct xswitch_device *xsw; + unsigned int npads; + unsigned int i; + int ret; + + xsw = devm_kzalloc(&pdev->dev, sizeof(*xsw), GFP_KERNEL); + if (!xsw) + return -ENOMEM; + + xsw->xvip.dev = &pdev->dev; + + ret = xsw_parse_of(xsw); + if (ret < 0) + return ret; + + ret = xvip_init_resources(&xsw->xvip); + if (ret < 0) + return ret; + + /* Initialize V4L2 subdevice and media entity. Pad numbers depend on the + * number of pads. + */ + npads = xsw->nsinks + xsw->nsources; + xsw->pads = devm_kzalloc(&pdev->dev, npads * sizeof(*xsw->pads), + GFP_KERNEL); + if (!xsw->pads) + goto error; + + for (i = 0; i < xsw->nsinks; ++i) + xsw->pads[i].flags = MEDIA_PAD_FL_SINK; + for (; i < npads; ++i) + xsw->pads[i].flags = MEDIA_PAD_FL_SOURCE; + + xsw->formats = devm_kzalloc(&pdev->dev, + xsw->nsinks * sizeof(*xsw->formats), + GFP_KERNEL); + if (!xsw->formats) + goto error; + + for (i = 0; i < xsw->nsources; ++i) + xsw->routing[i] = i < xsw->nsinks ? i : -1; + + subdev = &xsw->xvip.subdev; + v4l2_subdev_init(subdev, &xsw_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xsw_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xsw); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->entity.ops = &xsw_media_ops; + + xsw_init_formats(subdev, NULL); + + ret = media_entity_pads_init(&subdev->entity, npads, xsw->pads); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, xsw); + + xvip_print_version(&xsw->xvip); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + return 0; + +error: + media_entity_cleanup(&subdev->entity); + xvip_cleanup_resources(&xsw->xvip); + return ret; +} + +static int xsw_remove(struct platform_device *pdev) +{ + struct xswitch_device *xsw = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xsw->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + + xvip_cleanup_resources(&xsw->xvip); + + return 0; +} + +static const struct of_device_id xsw_of_id_table[] = { + { .compatible = "xlnx,v-switch-1.0" }, + { } +}; +MODULE_DEVICE_TABLE(of, xsw_of_id_table); + +static struct platform_driver xsw_driver = { + .driver = { + .name = "xilinx-switch", + .of_match_table = xsw_of_id_table, + }, + .probe = xsw_probe, + .remove = xsw_remove, +}; + +module_platform_driver(xsw_driver); + +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); +MODULE_DESCRIPTION("Xilinx Video Switch Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c index ed01bedb5db6..f840bc098d9e 100644 --- a/drivers/media/platform/xilinx/xilinx-tpg.c +++ b/drivers/media/platform/xilinx/xilinx-tpg.c @@ -20,6 +20,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-subdev.h> +#include "xilinx-hls-common.h" #include "xilinx-vip.h" #include "xilinx-vtc.h" @@ -58,6 +59,36 @@ #define XTPG_BAYER_PHASE_BGGR 3 #define XTPG_BAYER_PHASE_OFF 4 +/* TPG v7 is a completely redesigned IP using Vivado HLS + * having a different AXI4-Lite interface + */ +#define XTPG_HLS_BG_PATTERN 0x0020 +#define XTPG_HLS_FG_PATTERN 0x0028 +#define XTPG_HLS_FG_PATTERN_CROSS_HAIR (1 << 1) +#define XTPG_HLS_MASK_ID 0x0030 +#define XTPG_HLS_MOTION_SPEED 0x0038 +#define XTPG_HLS_COLOR_FORMAT 0x0040 +#define XTPG_HLS_COLOR_FORMAT_RGB 0 +#define XTPG_HLS_COLOR_FORMAT_YUV_444 1 +#define XTPG_HLS_COLOR_FORMAT_YUV_422 2 +#define XTPG_HLS_COLOR_FORMAT_YUV_420 3 +#define XTPG_HLS_CROSS_HAIR_HOR 0x0048 +#define XTPG_HLS_CROSS_HAIR_VER 0x0050 +#define XTPG_HLS_ZPLATE_HOR_CNTL_START 0x0058 +#define XTPG_HLS_ZPLATE_HOR_CNTL_DELTA 0x0060 +#define XTPG_HLS_ZPLATE_VER_CNTL_START 0x0068 +#define XTPG_HLS_ZPLATE_VER_CNTL_DELTA 0x0070 +#define XTPG_HLS_BOX_SIZE 0x0078 +#define XTPG_HLS_BOX_COLOR_RED_CB 0x0080 +#define XTPG_HLS_BOX_COLOR_GREEN_CR 0x0088 +#define XTPG_HLS_BOX_COLOR_BLUE_Y 0x0090 +#define XTPG_HLS_ENABLE_INPUT 0x0098 +#define XTPG_HLS_USE_INPUT_VID_STREAM (1 << 0) +#define XTPG_HLS_PASS_THRU_START_X 0x00a0 +#define XTPG_HLS_PASS_THRU_START_Y 0x00a8 +#define XTPG_HLS_PASS_THRU_END_X 0x00b0 +#define XTPG_HLS_PASS_THRU_END_Y 0x00b8 + /* * The minimum blanking value is one clock cycle for the front porch, one clock * cycle for the sync pulse and one clock cycle for the back porch. @@ -67,6 +98,15 @@ #define XTPG_MIN_VBLANK 3 #define XTPG_MAX_VBLANK (XVTC_MAX_VSIZE - XVIP_MIN_HEIGHT) +#define XTPG_MIN_WIDTH (64) +#define XTPG_MIN_HEIGHT (64) +#define XTPG_MAX_WIDTH (10328) +#define XTPG_MAX_HEIGHT (7760) + +#define XTPG_MIN_PPC 1 + +#define XTPG_MIN_FRM_INT 1 + /** * struct xtpg_device - Xilinx Test Pattern Generator device structure * @xvip: Xilinx Video IP device @@ -82,8 +122,15 @@ * @vblank: vertical blanking control * @pattern: test pattern control * @streaming: is the video stream active + * @is_hls: whether the IP core is HLS based * @vtc: video timing controller * @vtmux_gpio: video timing mux GPIO + * @rst_gpio: reset IP core GPIO + * @max_width: Maximum width supported by this instance + * @max_height: Maximum height supported by this instance + * @fi_d: frame interval denominator + * @fi_n: frame interval numerator + * @ppc: Pixels per clock control */ struct xtpg_device { struct xvip_device xvip; @@ -102,9 +149,17 @@ struct xtpg_device { struct v4l2_ctrl *vblank; struct v4l2_ctrl *pattern; bool streaming; + bool is_hls; struct xvtc_device *vtc; struct gpio_desc *vtmux_gpio; + struct gpio_desc *rst_gpio; + + u32 max_width; + u32 max_height; + u32 fi_d; + u32 fi_n; + u32 ppc; }; static inline struct xtpg_device *to_tpg(struct v4l2_subdev *subdev) @@ -128,6 +183,32 @@ static u32 xtpg_get_bayer_phase(unsigned int code) } } +static void xtpg_config_vtc(struct xtpg_device *xtpg, int width, int height) +{ + + struct xvtc_config config = { + .hblank_start = width / xtpg->ppc, + .hsync_start = width / xtpg->ppc + 1, + .vblank_start = height, + .vsync_start = height + 1, + .fps = xtpg->fi_d / xtpg->fi_n, + }; + unsigned int htotal; + unsigned int vtotal; + + htotal = min_t(unsigned int, XVTC_MAX_HSIZE, + (v4l2_ctrl_g_ctrl(xtpg->hblank) + width) / xtpg->ppc); + vtotal = min_t(unsigned int, XVTC_MAX_VSIZE, + v4l2_ctrl_g_ctrl(xtpg->vblank) + height); + + config.hsync_end = htotal - 1; + config.hsize = htotal; + config.vsync_end = vtotal - 1; + config.vsize = vtotal; + + xvtc_generator_start(xtpg->vtc, &config); +} + static void __xtpg_update_pattern_control(struct xtpg_device *xtpg, bool passthrough, bool pattern) { @@ -164,6 +245,33 @@ static void xtpg_update_pattern_control(struct xtpg_device *xtpg, * V4L2 Subdevice Video Operations */ +static int xtpg_g_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct xtpg_device *xtpg = to_tpg(subdev); + + fi->interval.numerator = xtpg->fi_n; + fi->interval.denominator = xtpg->fi_d; + + return 0; +} + +static int xtpg_s_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct xtpg_device *xtpg = to_tpg(subdev); + + if (!fi->interval.numerator || !fi->interval.denominator) { + xtpg->fi_n = XTPG_MIN_FRM_INT; + xtpg->fi_d = XTPG_MIN_FRM_INT; + } else { + xtpg->fi_n = fi->interval.numerator; + xtpg->fi_d = fi->interval.denominator; + } + + return 0; +} + static int xtpg_s_stream(struct v4l2_subdev *subdev, int enable) { struct xtpg_device *xtpg = to_tpg(subdev); @@ -173,7 +281,20 @@ static int xtpg_s_stream(struct v4l2_subdev *subdev, int enable) u32 bayer_phase; if (!enable) { - xvip_stop(&xtpg->xvip); + if (!xtpg->is_hls) { + xvip_stop(&xtpg->xvip); + } else { + /* + * There is an known issue in TPG v7.0 that on + * resolution change it doesn't generates pattern + * correctly i.e some hor/ver offset is added. + * As a workaround issue reset on stop. + */ + gpiod_set_value_cansleep(xtpg->rst_gpio, 0x1); + gpiod_set_value_cansleep(xtpg->rst_gpio, 0x0); + v4l2_ctrl_handler_setup(&xtpg->ctrl_handler); + } + if (xtpg->vtc) xvtc_generator_stop(xtpg->vtc); @@ -182,31 +303,36 @@ static int xtpg_s_stream(struct v4l2_subdev *subdev, int enable) return 0; } - xvip_set_frame_size(&xtpg->xvip, &xtpg->formats[0]); - - if (xtpg->vtc) { - struct xvtc_config config = { - .hblank_start = width, - .hsync_start = width + 1, - .vblank_start = height, - .vsync_start = height + 1, - }; - unsigned int htotal; - unsigned int vtotal; - - htotal = min_t(unsigned int, XVTC_MAX_HSIZE, - v4l2_ctrl_g_ctrl(xtpg->hblank) + width); - vtotal = min_t(unsigned int, XVTC_MAX_VSIZE, - v4l2_ctrl_g_ctrl(xtpg->vblank) + height); - - config.hsync_end = htotal - 1; - config.hsize = htotal; - config.vsync_end = vtotal - 1; - config.vsize = vtotal; - - xvtc_generator_start(xtpg->vtc, &config); + if (xtpg->is_hls) { + u32 fmt = 0; + + switch (xtpg->formats[0].code) { + case MEDIA_BUS_FMT_VYYUYY8_1X24: + case MEDIA_BUS_FMT_VYYUYY10_4X20: + fmt = XTPG_HLS_COLOR_FORMAT_YUV_420; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + fmt = XTPG_HLS_COLOR_FORMAT_YUV_422; + break; + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_VUY10_1X30: + fmt = XTPG_HLS_COLOR_FORMAT_YUV_444; + break; + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RBG101010_1X30: + fmt = XTPG_HLS_COLOR_FORMAT_RGB; + break; + } + xvip_write(&xtpg->xvip, XTPG_HLS_COLOR_FORMAT, fmt); + xvip_write(&xtpg->xvip, XHLS_REG_COLS, width); + xvip_write(&xtpg->xvip, XHLS_REG_ROWS, height); + } else { + xvip_set_frame_size(&xtpg->xvip, &xtpg->formats[0]); } + if (xtpg->vtc) + xtpg_config_vtc(xtpg, width, height); /* * Configure the bayer phase and video timing mux based on the * operation mode (passthrough or test pattern generation). The test @@ -215,7 +341,11 @@ static int xtpg_s_stream(struct v4l2_subdev *subdev, int enable) */ mutex_lock(xtpg->ctrl_handler.lock); - xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + if (xtpg->is_hls) + xvip_write(&xtpg->xvip, XTPG_HLS_BG_PATTERN, + xtpg->pattern->cur.val); + else + xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, XTPG_PATTERN_MASK, xtpg->pattern->cur.val); /* @@ -229,18 +359,26 @@ static int xtpg_s_stream(struct v4l2_subdev *subdev, int enable) mutex_unlock(xtpg->ctrl_handler.lock); - /* - * For TPG v5.0, the bayer phase needs to be off for the pass through - * mode, otherwise the external input would be subsampled. - */ - bayer_phase = passthrough ? XTPG_BAYER_PHASE_OFF - : xtpg_get_bayer_phase(xtpg->formats[0].code); - xvip_write(&xtpg->xvip, XTPG_BAYER_PHASE, bayer_phase); - if (xtpg->vtmux_gpio) gpiod_set_value_cansleep(xtpg->vtmux_gpio, !passthrough); - xvip_start(&xtpg->xvip); + if (xtpg->is_hls) { + xvip_set(&xtpg->xvip, XTPG_HLS_ENABLE_INPUT, + XTPG_HLS_USE_INPUT_VID_STREAM); + xvip_set(&xtpg->xvip, XVIP_CTRL_CONTROL, + XHLS_REG_CTRL_AUTO_RESTART | + XVIP_CTRL_CONTROL_SW_ENABLE); + } else { + /* + * For TPG v5.0, the bayer phase needs to be off for the pass + * through mode, otherwise the external input would + * be subsampled. + */ + bayer_phase = passthrough ? XTPG_BAYER_PHASE_OFF + : xtpg_get_bayer_phase(xtpg->formats[0].code); + xvip_write(&xtpg->xvip, XTPG_BAYER_PHASE, bayer_phase); + xvip_start(&xtpg->xvip); + } return 0; } @@ -300,7 +438,27 @@ static int xtpg_set_format(struct v4l2_subdev *subdev, __format->code = fmt->format.code; } - xvip_set_format_size(__format, fmt); + if (xtpg->is_hls) { + switch (fmt->format.code) { + case MEDIA_BUS_FMT_VYYUYY8_1X24: + case MEDIA_BUS_FMT_VYYUYY10_4X20: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_VUY10_1X30: + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RBG101010_1X30: + __format->code = fmt->format.code; + break; + default: + __format->code = xtpg->default_format.code; + } + } + + __format->width = clamp_t(unsigned int, fmt->format.width, + XTPG_MIN_WIDTH, xtpg->max_width); + __format->height = clamp_t(unsigned int, fmt->format.height, + XTPG_MIN_HEIGHT, xtpg->max_height); fmt->format = *__format; @@ -322,6 +480,7 @@ static int xtpg_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_size_enum *fse) { struct v4l2_mbus_framefmt *format; + struct xtpg_device *xtpg = to_tpg(subdev); format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); @@ -330,12 +489,13 @@ static int xtpg_enum_frame_size(struct v4l2_subdev *subdev, /* Min / max values for pad 0 is always fixed in both one and two pads * modes. In two pads mode, the source pad(= 1) size is identical to - * the sink pad size */ + * the sink pad size. + */ if (fse->pad == 0) { - fse->min_width = XVIP_MIN_WIDTH; - fse->max_width = XVIP_MAX_WIDTH; - fse->min_height = XVIP_MIN_HEIGHT; - fse->max_height = XVIP_MAX_HEIGHT; + fse->min_width = XTPG_MIN_WIDTH; + fse->max_width = xtpg->max_width; + fse->min_height = XTPG_MIN_HEIGHT; + fse->max_height = xtpg->max_height; } else { fse->min_width = format->width; fse->max_width = format->width; @@ -374,8 +534,12 @@ static int xtpg_s_ctrl(struct v4l2_ctrl *ctrl) ctrl_handler); switch (ctrl->id) { case V4L2_CID_TEST_PATTERN: - xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, - XTPG_PATTERN_MASK, ctrl->val); + if (xtpg->is_hls) + xvip_write(&xtpg->xvip, XTPG_HLS_BG_PATTERN, + ctrl->val); + else + xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_MASK, ctrl->val); return 0; case V4L2_CID_XILINX_TPG_CROSS_HAIRS: xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, @@ -386,10 +550,13 @@ static int xtpg_s_ctrl(struct v4l2_ctrl *ctrl) XTPG_PATTERN_CONTROL_MOVING_BOX, ctrl->val); return 0; case V4L2_CID_XILINX_TPG_COLOR_MASK: - xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, - XTPG_PATTERN_CONTROL_COLOR_MASK_MASK, - ctrl->val << - XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT); + if (xtpg->is_hls) + xvip_write(&xtpg->xvip, XTPG_HLS_MASK_ID, ctrl->val); + else + xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, + XTPG_PATTERN_CONTROL_COLOR_MASK_MASK, + ctrl->val << + XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT); return 0; case V4L2_CID_XILINX_TPG_STUCK_PIXEL: xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, @@ -404,43 +571,85 @@ static int xtpg_s_ctrl(struct v4l2_ctrl *ctrl) XTPG_PATTERN_CONTROL_MOTION, ctrl->val); return 0; case V4L2_CID_XILINX_TPG_MOTION_SPEED: - xvip_write(&xtpg->xvip, XTPG_MOTION_SPEED, ctrl->val); + if (xtpg->is_hls) + xvip_write(&xtpg->xvip, XTPG_HLS_MOTION_SPEED, + ctrl->val); + else + xvip_write(&xtpg->xvip, XTPG_MOTION_SPEED, ctrl->val); return 0; case V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW: - xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, - XTPG_CROSS_HAIRS_ROW_MASK, - ctrl->val << XTPG_CROSS_HAIRS_ROW_SHIFT); + if (xtpg->is_hls) + xvip_write(&xtpg->xvip, XTPG_HLS_CROSS_HAIR_HOR, + ctrl->val); + else + xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, + XTPG_CROSS_HAIRS_ROW_MASK, + ctrl->val << + XTPG_CROSS_HAIRS_ROW_SHIFT); return 0; case V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN: - xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, - XTPG_CROSS_HAIRS_COLUMN_MASK, - ctrl->val << XTPG_CROSS_HAIRS_COLUMN_SHIFT); + if (xtpg->is_hls) + xvip_write(&xtpg->xvip, XTPG_HLS_CROSS_HAIR_VER, + ctrl->val); + else + xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, + XTPG_CROSS_HAIRS_COLUMN_MASK, + ctrl->val << + XTPG_CROSS_HAIRS_COLUMN_SHIFT); return 0; case V4L2_CID_XILINX_TPG_ZPLATE_HOR_START: - xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, - XTPG_ZPLATE_START_MASK, - ctrl->val << XTPG_ZPLATE_START_SHIFT); + if (xtpg->is_hls) + xvip_write(&xtpg->xvip, XTPG_HLS_ZPLATE_HOR_CNTL_START, + ctrl->val); + else + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, + XTPG_ZPLATE_START_MASK, + ctrl->val << XTPG_ZPLATE_START_SHIFT); return 0; case V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED: - xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, - XTPG_ZPLATE_SPEED_MASK, - ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); + if (xtpg->is_hls) + xvip_write(&xtpg->xvip, XTPG_HLS_ZPLATE_HOR_CNTL_DELTA, + ctrl->val); + else + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, + XTPG_ZPLATE_SPEED_MASK, + ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); return 0; case V4L2_CID_XILINX_TPG_ZPLATE_VER_START: - xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, - XTPG_ZPLATE_START_MASK, - ctrl->val << XTPG_ZPLATE_START_SHIFT); + if (xtpg->is_hls) + xvip_write(&xtpg->xvip, XTPG_HLS_ZPLATE_VER_CNTL_START, + ctrl->val); + else + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, + XTPG_ZPLATE_START_MASK, + ctrl->val << XTPG_ZPLATE_START_SHIFT); return 0; case V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED: - xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, - XTPG_ZPLATE_SPEED_MASK, - ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); + if (xtpg->is_hls) + xvip_write(&xtpg->xvip, XTPG_HLS_ZPLATE_VER_CNTL_DELTA, + ctrl->val); + else + xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, + XTPG_ZPLATE_SPEED_MASK, + ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); return 0; case V4L2_CID_XILINX_TPG_BOX_SIZE: - xvip_write(&xtpg->xvip, XTPG_BOX_SIZE, ctrl->val); + if (xtpg->is_hls) + xvip_write(&xtpg->xvip, XTPG_HLS_BOX_SIZE, ctrl->val); + else + xvip_write(&xtpg->xvip, XTPG_BOX_SIZE, ctrl->val); return 0; case V4L2_CID_XILINX_TPG_BOX_COLOR: - xvip_write(&xtpg->xvip, XTPG_BOX_COLOR, ctrl->val); + if (xtpg->is_hls) { + xvip_write(&xtpg->xvip, XTPG_HLS_BOX_COLOR_RED_CB, + ctrl->val >> 16); + xvip_write(&xtpg->xvip, XTPG_HLS_BOX_COLOR_GREEN_CR, + ctrl->val >> 8); + xvip_write(&xtpg->xvip, XTPG_HLS_BOX_COLOR_BLUE_Y, + ctrl->val); + } else { + xvip_write(&xtpg->xvip, XTPG_BOX_COLOR, ctrl->val); + } return 0; case V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH: xvip_write(&xtpg->xvip, XTPG_STUCK_PIXEL_THRESH, ctrl->val); @@ -448,6 +657,9 @@ static int xtpg_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_XILINX_TPG_NOISE_GAIN: xvip_write(&xtpg->xvip, XTPG_NOISE_GAIN, ctrl->val); return 0; + case V4L2_CID_XILINX_TPG_HLS_FG_PATTERN: + xvip_write(&xtpg->xvip, XTPG_HLS_FG_PATTERN, ctrl->val); + return 0; } return 0; @@ -461,6 +673,8 @@ static const struct v4l2_subdev_core_ops xtpg_core_ops = { }; static const struct v4l2_subdev_video_ops xtpg_video_ops = { + .g_frame_interval = xtpg_g_frame_interval, + .s_frame_interval = xtpg_s_frame_interval, .s_stream = xtpg_s_stream, }; @@ -505,60 +719,51 @@ static const char *const xtpg_pattern_strings[] = { "Black/White Checker Board", }; -static struct v4l2_ctrl_config xtpg_ctrls[] = { +static const char *const xtpg_hls_pattern_strings[] = { + "Passthrough", + "Horizontal Ramp", + "Vertical Ramp", + "Temporal Ramp", + "Solid Red", + "Solid Green", + "Solid Blue", + "Solid Black", + "Solid White", + "Color Bars", + "Zone Plate", + "Tartan Color Bars", + "Cross Hatch", + "Color Sweep", + "Vertical/Horizontal Ramps", + "Black/White Checker Board", + "PseudoRandom", +}; + +static const char *const xtpg_hls_fg_strings[] = { + "No Overlay", + "Moving Box", + "Cross Hairs", +}; + +static const struct v4l2_ctrl_config xtpg_hls_fg_ctrl = { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_HLS_FG_PATTERN, + .name = "Test Pattern: Foreground Pattern", + .type = V4L2_CTRL_TYPE_MENU, + .min = 0, + .max = ARRAY_SIZE(xtpg_hls_fg_strings) - 1, + .qmenu = xtpg_hls_fg_strings, +}; + +static struct v4l2_ctrl_config xtpg_common_ctrls[] = { { - .ops = &xtpg_ctrl_ops, - .id = V4L2_CID_XILINX_TPG_CROSS_HAIRS, - .name = "Test Pattern: Cross Hairs", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .min = false, - .max = true, - .step = 1, - .def = 0, - }, { - .ops = &xtpg_ctrl_ops, - .id = V4L2_CID_XILINX_TPG_MOVING_BOX, - .name = "Test Pattern: Moving Box", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .min = false, - .max = true, - .step = 1, - .def = 0, - }, { - .ops = &xtpg_ctrl_ops, - .id = V4L2_CID_XILINX_TPG_COLOR_MASK, - .name = "Test Pattern: Color Mask", - .type = V4L2_CTRL_TYPE_BITMASK, - .min = 0, - .max = 0xf, - .def = 0, - }, { - .ops = &xtpg_ctrl_ops, - .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL, - .name = "Test Pattern: Stuck Pixel", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .min = false, - .max = true, - .step = 1, - .def = 0, - }, { - .ops = &xtpg_ctrl_ops, - .id = V4L2_CID_XILINX_TPG_NOISE, - .name = "Test Pattern: Noise", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .min = false, - .max = true, - .step = 1, - .def = 0, - }, { - .ops = &xtpg_ctrl_ops, - .id = V4L2_CID_XILINX_TPG_MOTION, - .name = "Test Pattern: Motion", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .min = false, - .max = true, - .step = 1, - .def = 0, + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_COLOR_MASK, + .name = "Test Pattern: Color Mask", + .type = V4L2_CTRL_TYPE_BITMASK, + .min = 0, + .max = 0x7, + .def = 0, }, { .ops = &xtpg_ctrl_ops, .id = V4L2_CID_XILINX_TPG_MOTION_SPEED, @@ -642,12 +847,61 @@ static struct v4l2_ctrl_config xtpg_ctrls[] = { }, { .ops = &xtpg_ctrl_ops, .id = V4L2_CID_XILINX_TPG_BOX_COLOR, - .name = "Test Pattern: Box Color(RGB)", + .name = "Test Pattern: Box Color(RGB/YCbCr)", .type = V4L2_CTRL_TYPE_INTEGER, .min = 0, .max = (1 << 24) - 1, .step = 1, .def = 0, + }, +}; + +static struct v4l2_ctrl_config xtpg_ctrls[] = { + { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_CROSS_HAIRS, + .name = "Test Pattern: Cross Hairs", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_MOVING_BOX, + .name = "Test Pattern: Moving Box", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL, + .name = "Test Pattern: Stuck Pixel", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_NOISE, + .name = "Test Pattern: Noise", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, + }, { + .ops = &xtpg_ctrl_ops, + .id = V4L2_CID_XILINX_TPG_MOTION, + .name = "Test Pattern: Motion", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = false, + .max = true, + .step = 1, + .def = 0, }, { .ops = &xtpg_ctrl_ops, .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH, @@ -713,6 +967,49 @@ static int xtpg_parse_of(struct xtpg_device *xtpg) struct device_node *port; unsigned int nports = 0; bool has_endpoint = false; + int ret; + + if (!of_device_is_compatible(dev->of_node, "xlnx,v-tpg-5.0")) + xtpg->is_hls = true; + + ret = of_property_read_u32(node, "xlnx,max-height", + &xtpg->max_height); + if (ret < 0) { + if (of_device_is_compatible(dev->of_node, "xlnx,v-tpg-8.0")) { + dev_err(dev, "xlnx,max-height dt property is missing!"); + return -EINVAL; + } + xtpg->max_height = XTPG_MAX_HEIGHT; + } else if (xtpg->max_height > XTPG_MAX_HEIGHT || + xtpg->max_height < XTPG_MIN_HEIGHT) { + dev_err(dev, "Invalid height in dt"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "xlnx,max-width", + &xtpg->max_width); + if (ret < 0) { + if (of_device_is_compatible(dev->of_node, "xlnx,v-tpg-8.0")) { + dev_err(dev, "xlnx,max-width dt property is missing!"); + return -EINVAL; + } + xtpg->max_width = XTPG_MAX_WIDTH; + } else if (xtpg->max_width > XTPG_MAX_WIDTH || + xtpg->max_width < XTPG_MIN_WIDTH) { + dev_err(dev, "Invalid width in dt"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "xlnx,ppc", + &xtpg->ppc); + if (ret < 0) { + xtpg->ppc = XTPG_MIN_PPC; + dev_dbg(dev, "failed to read ppc in dt\n"); + } else if ((xtpg->ppc != 1) && (xtpg->ppc != 2) && + (xtpg->ppc != 4) && (xtpg->ppc != 8)) { + dev_err(dev, "Invalid ppc config in dt\n"); + return -EINVAL; + } ports = of_get_child_by_name(node, "ports"); if (ports == NULL) @@ -769,6 +1066,7 @@ static int xtpg_probe(struct platform_device *pdev) struct v4l2_subdev *subdev; struct xtpg_device *xtpg; u32 i, bayer_phase; + u32 npatterns; int ret; xtpg = devm_kzalloc(&pdev->dev, sizeof(*xtpg), GFP_KERNEL); @@ -792,14 +1090,29 @@ static int xtpg_probe(struct platform_device *pdev) goto error_resource; } + if (xtpg->is_hls) { + xtpg->rst_gpio = devm_gpiod_get(&pdev->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(xtpg->rst_gpio)) { + ret = PTR_ERR(xtpg->rst_gpio); + goto error_resource; + } + } + xtpg->vtc = xvtc_of_get(pdev->dev.of_node); if (IS_ERR(xtpg->vtc)) { ret = PTR_ERR(xtpg->vtc); goto error_resource; } - /* Reset and initialize the core */ - xvip_reset(&xtpg->xvip); + /* + * Reset and initialize the core. For TPG HLS version there + * is no SW_RESET bit hence using GPIO based reset. + */ + if (xtpg->is_hls) + gpiod_set_value_cansleep(xtpg->rst_gpio, 0x0); + else + xvip_reset(&xtpg->xvip); /* Initialize V4L2 subdevice and media entity. Pad numbers depend on the * number of pads. @@ -815,11 +1128,23 @@ static int xtpg_probe(struct platform_device *pdev) xtpg->default_format.code = xtpg->vip_format->code; xtpg->default_format.field = V4L2_FIELD_NONE; xtpg->default_format.colorspace = V4L2_COLORSPACE_SRGB; - xvip_get_frame_size(&xtpg->xvip, &xtpg->default_format); - bayer_phase = xtpg_get_bayer_phase(xtpg->vip_format->code); - if (bayer_phase != XTPG_BAYER_PHASE_OFF) - xtpg->bayer = true; + if (xtpg->is_hls) { + npatterns = ARRAY_SIZE(xtpg_hls_pattern_strings); + xtpg->default_format.width = xvip_read(&xtpg->xvip, + XHLS_REG_COLS); + xtpg->default_format.height = xvip_read(&xtpg->xvip, + XHLS_REG_ROWS); + } else { + npatterns = ARRAY_SIZE(xtpg_pattern_strings); + xvip_get_frame_size(&xtpg->xvip, &xtpg->default_format); + } + + if (!xtpg->is_hls) { + bayer_phase = xtpg_get_bayer_phase(xtpg->vip_format->code); + if (bayer_phase != XTPG_BAYER_PHASE_OFF) + xtpg->bayer = true; + } xtpg->formats[0] = xtpg->default_format; if (xtpg->npads == 2) @@ -839,7 +1164,13 @@ static int xtpg_probe(struct platform_device *pdev) if (ret < 0) goto error; - v4l2_ctrl_handler_init(&xtpg->ctrl_handler, 3 + ARRAY_SIZE(xtpg_ctrls)); + if (xtpg->is_hls) + v4l2_ctrl_handler_init(&xtpg->ctrl_handler, 4 + + ARRAY_SIZE(xtpg_common_ctrls)); + else + v4l2_ctrl_handler_init(&xtpg->ctrl_handler, 3 + + ARRAY_SIZE(xtpg_common_ctrls) + + ARRAY_SIZE(xtpg_ctrls)); xtpg->vblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops, V4L2_CID_VBLANK, XTPG_MIN_VBLANK, @@ -847,19 +1178,41 @@ static int xtpg_probe(struct platform_device *pdev) xtpg->hblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops, V4L2_CID_HBLANK, XTPG_MIN_HBLANK, XTPG_MAX_HBLANK, 1, 100); - xtpg->pattern = v4l2_ctrl_new_std_menu_items(&xtpg->ctrl_handler, - &xtpg_ctrl_ops, V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(xtpg_pattern_strings) - 1, - 1, 9, xtpg_pattern_strings); - for (i = 0; i < ARRAY_SIZE(xtpg_ctrls); i++) - v4l2_ctrl_new_custom(&xtpg->ctrl_handler, &xtpg_ctrls[i], NULL); + if (xtpg->is_hls) { + xtpg->pattern = + v4l2_ctrl_new_std_menu_items(&xtpg->ctrl_handler, + &xtpg_ctrl_ops, + V4L2_CID_TEST_PATTERN, + npatterns - 1, + 1, 9, + xtpg_hls_pattern_strings); + v4l2_ctrl_new_custom(&xtpg->ctrl_handler, + &xtpg_hls_fg_ctrl, NULL); + } else { + xtpg->pattern = + v4l2_ctrl_new_std_menu_items(&xtpg->ctrl_handler, + &xtpg_ctrl_ops, + V4L2_CID_TEST_PATTERN, + npatterns - 1, + 1, 9, + xtpg_pattern_strings); + + for (i = 0; i < ARRAY_SIZE(xtpg_ctrls); i++) + v4l2_ctrl_new_custom(&xtpg->ctrl_handler, + &xtpg_ctrls[i], NULL); + } + + for (i = 0; i < ARRAY_SIZE(xtpg_common_ctrls); i++) + v4l2_ctrl_new_custom(&xtpg->ctrl_handler, + &xtpg_common_ctrls[i], NULL); if (xtpg->ctrl_handler.error) { dev_err(&pdev->dev, "failed to add controls\n"); ret = xtpg->ctrl_handler.error; goto error; } + subdev->ctrl_handler = &xtpg->ctrl_handler; xtpg_update_pattern_control(xtpg, true, true); @@ -874,6 +1227,10 @@ static int xtpg_probe(struct platform_device *pdev) xvip_print_version(&xtpg->xvip); + /* Initialize default frame interval */ + xtpg->fi_n = 1; + xtpg->fi_d = 30; + ret = v4l2_async_register_subdev(subdev); if (ret < 0) { dev_err(&pdev->dev, "failed to register subdev\n"); @@ -909,6 +1266,8 @@ static SIMPLE_DEV_PM_OPS(xtpg_pm_ops, xtpg_pm_suspend, xtpg_pm_resume); static const struct of_device_id xtpg_of_id_table[] = { { .compatible = "xlnx,v-tpg-5.0" }, + { .compatible = "xlnx,v-tpg-7.0" }, + { .compatible = "xlnx,v-tpg-8.0" }, { } }; MODULE_DEVICE_TABLE(of, xtpg_of_id_table); diff --git a/drivers/media/platform/xilinx/xilinx-vip.c b/drivers/media/platform/xilinx/xilinx-vip.c index 08a825c3a3f6..466d6f27af1d 100644 --- a/drivers/media/platform/xilinx/xilinx-vip.c +++ b/drivers/media/platform/xilinx/xilinx-vip.c @@ -24,22 +24,102 @@ */ static const struct xvip_video_format xvip_video_formats[] = { + { XVIP_VF_YUV_420, 8, NULL, MEDIA_BUS_FMT_VYYUYY8_1X24, + 1, 12, V4L2_PIX_FMT_NV12, 2, 1, 1, 2, "4:2:0, semi-planar, YUV" }, + { XVIP_VF_YUV_420, 8, NULL, MEDIA_BUS_FMT_VYYUYY8_1X24, + 1, 12, V4L2_PIX_FMT_NV12M, 2, 2, 1, 2, "4:2:0, 2-plane non-cont" }, + { XVIP_VF_YUV_420, 10, NULL, MEDIA_BUS_FMT_VYYUYY10_4X20, + 1, 12, V4L2_PIX_FMT_XV15, 2, 1, 2, 2, "4:2:0, 10-bit 2-plane cont" }, + { XVIP_VF_YUV_420, 10, NULL, MEDIA_BUS_FMT_VYYUYY10_4X20, + 1, 12, V4L2_PIX_FMT_XV15M, 2, 2, 1, 2, "4:2:0, 10-bit 2-plane non-cont" }, + { XVIP_VF_YUV_420, 12, NULL, MEDIA_BUS_FMT_UYYVYY12_4X24, + 1, 12, V4L2_PIX_FMT_X012, 2, 1, 2, 2, "4:2:0, 12-bit 2-plane cont" }, + { XVIP_VF_YUV_420, 12, NULL, MEDIA_BUS_FMT_UYYVYY12_4X24, + 1, 12, V4L2_PIX_FMT_X012M, 2, 2, 1, 2, "4:2:0, 12-bit 2-plane non-cont" }, + { XVIP_VF_YUV_420, 16, NULL, MEDIA_BUS_FMT_UYYVYY16_4X32, + 2, 12, V4L2_PIX_FMT_X016, 2, 1, 2, 2, "4:2:0, 16-bit 2-plane cont" }, + { XVIP_VF_YUV_420, 16, NULL, MEDIA_BUS_FMT_UYYVYY16_4X32, + 2, 12, V4L2_PIX_FMT_X016M, 2, 2, 1, 2, "4:2:0, 16-bit 2-plane non-cont" }, { XVIP_VF_YUV_422, 8, NULL, MEDIA_BUS_FMT_UYVY8_1X16, - 2, V4L2_PIX_FMT_YUYV, "4:2:2, packed, YUYV" }, + 2, 16, V4L2_PIX_FMT_YUYV, 1, 1, 2, 1, "4:2:2, packed, YUYV" }, + { XVIP_VF_VUY_422, 8, NULL, MEDIA_BUS_FMT_UYVY8_1X16, + 2, 16, V4L2_PIX_FMT_UYVY, 1, 1, 2, 1, "4:2:2, packed, UYVY" }, + { XVIP_VF_YUV_422, 8, NULL, MEDIA_BUS_FMT_UYVY8_1X16, + 1, 16, V4L2_PIX_FMT_NV16, 2, 1, 1, 1, "4:2:2, semi-planar, YUV" }, + { XVIP_VF_YUV_422, 8, NULL, MEDIA_BUS_FMT_UYVY8_1X16, + 1, 16, V4L2_PIX_FMT_NV16M, 2, 2, 1, 1, "4:2:2, 2-plane non-contiguous" }, + { XVIP_VF_YUV_422, 10, NULL, MEDIA_BUS_FMT_UYVY10_1X20, + 1, 16, V4L2_PIX_FMT_XV20, 2, 1, 2, 1, "4:2:2, 10-bit 2-plane cont" }, + { XVIP_VF_YUV_422, 10, NULL, MEDIA_BUS_FMT_UYVY10_1X20, + 1, 16, V4L2_PIX_FMT_XV20M, 2, 2, 1, 1, "4:2:2, 10-bit 2-plane non-cont" }, + { XVIP_VF_YUV_422, 12, NULL, MEDIA_BUS_FMT_UYVY12_1X24, + 1, 16, V4L2_PIX_FMT_X212, 2, 1, 2, 1, "4:2:2, 12-bit 2-plane cont" }, + { XVIP_VF_YUV_422, 12, NULL, MEDIA_BUS_FMT_UYVY12_1X24, + 1, 16, V4L2_PIX_FMT_X212M, 2, 2, 1, 1, "4:2:2, 12-bit 2-plane non-cont" }, + { XVIP_VF_YUV_422, 16, NULL, MEDIA_BUS_FMT_UYVY12_1X24, + 2, 16, V4L2_PIX_FMT_X216, 2, 1, 2, 1, "4:2:2, 16-bit 2-plane cont" }, + { XVIP_VF_YUV_422, 16, NULL, MEDIA_BUS_FMT_UYVY12_1X24, + 2, 16, V4L2_PIX_FMT_X216M, 2, 2, 1, 1, "4:2:2, 16-bit 2-plane non-cont" }, { XVIP_VF_YUV_444, 8, NULL, MEDIA_BUS_FMT_VUY8_1X24, - 3, V4L2_PIX_FMT_YUV444, "4:4:4, packed, YUYV" }, + 3, 24, V4L2_PIX_FMT_VUY24, 1, 1, 1, 1, "4:4:4, packed, YUYV" }, + { XVIP_VF_YUVX, 8, NULL, MEDIA_BUS_FMT_VUY8_1X24, + 4, 32, V4L2_PIX_FMT_XVUY32, 1, 1, 1, 1, "X:4:4:4, packed, YUYV" }, + { XVIP_VF_YUVX, 10, NULL, MEDIA_BUS_FMT_VUY10_1X30, + 3, 32, V4L2_PIX_FMT_XVUY10, 1, 1, 1, 1, "2:10:10:10, packed, XVUY" }, + { XVIP_VF_YUV_444, 12, NULL, MEDIA_BUS_FMT_VUY12_1X36, + 1, 24, V4L2_PIX_FMT_X412, 1, 1, 1, 1, "4:4:4, 12-bit 2-plane cont" }, + { XVIP_VF_YUV_444, 12, NULL, MEDIA_BUS_FMT_VUY12_1X36, + 1, 24, V4L2_PIX_FMT_X412M, 1, 1, 1, 1, "4:4:4, 12-bit 2-plane non-cont" }, + { XVIP_VF_YUV_444, 16, NULL, MEDIA_BUS_FMT_VUY16_1X48, + 2, 24, V4L2_PIX_FMT_X416, 1, 1, 1, 1, "4:4:4, 16-bit 2-plane cont" }, + { XVIP_VF_YUV_444, 16, NULL, MEDIA_BUS_FMT_VUY16_1X48, + 2, 24, V4L2_PIX_FMT_X416M, 1, 1, 1, 1, "4:4:4, 16-bit 2-plane non-cont" }, + { XVIP_VF_RBG, 8, NULL, MEDIA_BUS_FMT_RBG888_1X24, + 3, 24, V4L2_PIX_FMT_BGR24, 1, 1, 1, 1, "24-bit RGB" }, { XVIP_VF_RBG, 8, NULL, MEDIA_BUS_FMT_RBG888_1X24, - 3, 0, NULL }, + 3, 24, V4L2_PIX_FMT_RGB24, 1, 1, 1, 1, "24-bit RGB" }, + { XVIP_VF_BGRX, 8, NULL, MEDIA_BUS_FMT_RBG888_1X24, + 4, 32, V4L2_PIX_FMT_BGRX32, 1, 1, 1, 1, "x:8:8:8 RGB w/8 bits padding" }, + { XVIP_VF_XRGB, 8, NULL, MEDIA_BUS_FMT_RBG888_1X24, + 4, 32, V4L2_PIX_FMT_XBGR32, 1, 1, 1, 1, "8:8:8:x RGBx w/8 bits padding" }, + { XVIP_VF_XBGR, 10, NULL, MEDIA_BUS_FMT_RBG101010_1X30, + 3, 32, V4L2_PIX_FMT_XBGR30, 1, 1, 1, 1, "2:10:10:10, packed, XBGR" }, + { XVIP_VF_XBGR, 12, NULL, MEDIA_BUS_FMT_RBG121212_1X36, + 3, 40, V4L2_PIX_FMT_XBGR40, 1, 1, 1, 1, "4:12:12:12, packed, XBGR" }, + { XVIP_VF_RBG, 16, NULL, MEDIA_BUS_FMT_RBG161616_1X48, + 6, 48, V4L2_PIX_FMT_BGR48, 1, 1, 1, 1, "48-bit RGB" }, { XVIP_VF_MONO_SENSOR, 8, "mono", MEDIA_BUS_FMT_Y8_1X8, - 1, V4L2_PIX_FMT_GREY, "Greyscale 8-bit" }, + 1, 8, V4L2_PIX_FMT_GREY, 1, 1, 1, 1, "Greyscale 8-bit" }, + { XVIP_VF_Y_GREY, 10, NULL, MEDIA_BUS_FMT_Y10_1X10, + 4, 32, V4L2_PIX_FMT_XY10, 1, 1, 1, 1, "2:10:10:10, Grey, xY1Y2Y3Y4" }, + { XVIP_VF_Y_GREY, 12, NULL, MEDIA_BUS_FMT_Y12_1X12, + 1, 12, V4L2_PIX_FMT_XY12, 1, 1, 1, 1, "4:12:12:12, packed, xY1Y2Y3" }, + { XVIP_VF_Y_GREY, 16, NULL, MEDIA_BUS_FMT_Y16_1X16, + 2, 16, V4L2_PIX_FMT_Y16, 1, 1, 1, 1, "Greyscale 16-bit" }, { XVIP_VF_MONO_SENSOR, 8, "rggb", MEDIA_BUS_FMT_SRGGB8_1X8, - 1, V4L2_PIX_FMT_SRGGB8, "Bayer 8-bit RGGB" }, + 1, 8, V4L2_PIX_FMT_SGRBG8, 1, 1, 1, 1, "Bayer 8-bit RGGB" }, { XVIP_VF_MONO_SENSOR, 8, "grbg", MEDIA_BUS_FMT_SGRBG8_1X8, - 1, V4L2_PIX_FMT_SGRBG8, "Bayer 8-bit GRBG" }, + 1, 8, V4L2_PIX_FMT_SGRBG8, 1, 1, 1, 1, "Bayer 8-bit GRBG" }, { XVIP_VF_MONO_SENSOR, 8, "gbrg", MEDIA_BUS_FMT_SGBRG8_1X8, - 1, V4L2_PIX_FMT_SGBRG8, "Bayer 8-bit GBRG" }, + 1, 8, V4L2_PIX_FMT_SGBRG8, 1, 1, 1, 1, "Bayer 8-bit GBRG" }, { XVIP_VF_MONO_SENSOR, 8, "bggr", MEDIA_BUS_FMT_SBGGR8_1X8, - 1, V4L2_PIX_FMT_SBGGR8, "Bayer 8-bit BGGR" }, + 1, 8, V4L2_PIX_FMT_SBGGR8, 1, 1, 1, 1, "Bayer 8-bit BGGR" }, + { XVIP_VF_MONO_SENSOR, 12, "rggb", MEDIA_BUS_FMT_SRGGB12_1X12, + 1, 12, V4L2_PIX_FMT_SRGGB12, 1, 1, 1, 1, "Bayer 12-bit RGGB" }, + { XVIP_VF_MONO_SENSOR, 12, "grbg", MEDIA_BUS_FMT_SGRBG12_1X12, + 1, 12, V4L2_PIX_FMT_SGRBG12, 1, 1, 1, 1, "Bayer 12-bit GRBG" }, + { XVIP_VF_MONO_SENSOR, 12, "gbrg", MEDIA_BUS_FMT_SGBRG12_1X12, + 1, 12, V4L2_PIX_FMT_SGBRG12, 1, 1, 1, 1, "Bayer 12-bit GBRG" }, + { XVIP_VF_MONO_SENSOR, 12, "bggr", MEDIA_BUS_FMT_SBGGR12_1X12, + 1, 12, V4L2_PIX_FMT_SBGGR12, 1, 1, 1, 1, "Bayer 12-bit BGGR" }, + { XVIP_VF_MONO_SENSOR, 16, "rggb", MEDIA_BUS_FMT_SRGGB16_1X16, + 1, 16, V4L2_PIX_FMT_SRGGB16, 1, 1, 1, 1, "Bayer 16-bit RGGB" }, + { XVIP_VF_MONO_SENSOR, 16, "grbg", MEDIA_BUS_FMT_SGRBG16_1X16, + 1, 12, V4L2_PIX_FMT_SGRBG16, 1, 1, 1, 1, "Bayer 16-bit GRBG" }, + { XVIP_VF_MONO_SENSOR, 16, "gbrg", MEDIA_BUS_FMT_SGBRG16_1X16, + 1, 12, V4L2_PIX_FMT_SGBRG16, 1, 1, 1, 1, "Bayer 16-bit GBRG" }, + { XVIP_VF_MONO_SENSOR, 16, "bggr", MEDIA_BUS_FMT_SBGGR12_1X12, + 1, 12, V4L2_PIX_FMT_SBGGR16, 1, 1, 1, 1, "Bayer 16-bit BGGR" }, }; /** @@ -89,6 +169,87 @@ const struct xvip_video_format *xvip_get_format_by_fourcc(u32 fourcc) EXPORT_SYMBOL_GPL(xvip_get_format_by_fourcc); /** + * xvip_bpl_scaling_factor - Retrieve bpl scaling factor for a 4CC + * @fourcc: the format 4CC + * @numerator: returning numerator of scaling factor + * @denominator: returning denominator of scaling factor + * + * Return: Return numerator and denominator values by address + */ +void xvip_bpl_scaling_factor(u32 fourcc, u32 *numerator, u32 *denominator) +{ + switch (fourcc) { + case V4L2_PIX_FMT_XY10: + case V4L2_PIX_FMT_XV15: + case V4L2_PIX_FMT_XV20: + case V4L2_PIX_FMT_XV15M: + case V4L2_PIX_FMT_XV20M: + case V4L2_PIX_FMT_XBGR30: + case V4L2_PIX_FMT_XVUY10: + *numerator = 10; + *denominator = 8; + break; + case V4L2_PIX_FMT_XBGR40: + case V4L2_PIX_FMT_XY12: + case V4L2_PIX_FMT_X012: + case V4L2_PIX_FMT_X012M: + case V4L2_PIX_FMT_X212: + case V4L2_PIX_FMT_X212M: + case V4L2_PIX_FMT_X412: + case V4L2_PIX_FMT_X412M: + *numerator = 12; + *denominator = 8; + break; + default: + *numerator = 1; + *denominator = 1; + break; + } +} +EXPORT_SYMBOL_GPL(xvip_bpl_scaling_factor); + +/** + * xvip_width_padding_factor - Retrieve width's padding factor for a 4CC + * @fourcc: the format 4CC + * @numerator: returning numerator of padding factor + * @denominator: returning denominator of padding factor + * + * Return: Return numerator and denominator values by address + */ +void xvip_width_padding_factor(u32 fourcc, u32 *numerator, u32 *denominator) +{ + switch (fourcc) { + case V4L2_PIX_FMT_XY10: + case V4L2_PIX_FMT_XV15: + case V4L2_PIX_FMT_XV20: + case V4L2_PIX_FMT_XV15M: + case V4L2_PIX_FMT_XV20M: + case V4L2_PIX_FMT_XBGR30: + case V4L2_PIX_FMT_XVUY10: + /* 32 bits are required per 30 bits of data */ + *numerator = 32; + *denominator = 30; + break; + case V4L2_PIX_FMT_XBGR40: + case V4L2_PIX_FMT_XY12: + case V4L2_PIX_FMT_X012: + case V4L2_PIX_FMT_X012M: + case V4L2_PIX_FMT_X212: + case V4L2_PIX_FMT_X212M: + case V4L2_PIX_FMT_X412: + case V4L2_PIX_FMT_X412M: + *numerator = 40; + *denominator = 36; + break; + default: + *numerator = 1; + *denominator = 1; + break; + } +} +EXPORT_SYMBOL_GPL(xvip_width_padding_factor); + +/** * xvip_of_get_format - Parse a device tree node and return format information * @node: the device tree node * diff --git a/drivers/media/platform/xilinx/xilinx-vip.h b/drivers/media/platform/xilinx/xilinx-vip.h index ba939dd52818..55b994f0c26e 100644 --- a/drivers/media/platform/xilinx/xilinx-vip.h +++ b/drivers/media/platform/xilinx/xilinx-vip.h @@ -106,8 +106,13 @@ struct xvip_device { * @width: AXI4 format width in bits per component * @pattern: CFA pattern for Mono/Sensor formats * @code: media bus format code - * @bpp: bytes per pixel (when stored in memory) + * @bpl_factor: Bytes per line factor + * @bpp: bits per pixel * @fourcc: V4L2 pixel format FCC identifier + * @num_planes: number of planes w.r.t. color format + * @buffers: number of buffers per format + * @hsub: Horizontal sampling factor of Chroma + * @vsub: Vertical sampling factor of Chroma * @description: format description, suitable for userspace */ struct xvip_video_format { @@ -115,14 +120,21 @@ struct xvip_video_format { unsigned int width; const char *pattern; unsigned int code; + unsigned int bpl_factor; unsigned int bpp; u32 fourcc; + u8 num_planes; + u8 buffers; + u8 hsub; + u8 vsub; const char *description; }; const struct xvip_video_format *xvip_get_format_by_code(unsigned int code); const struct xvip_video_format *xvip_get_format_by_fourcc(u32 fourcc); const struct xvip_video_format *xvip_of_get_format(struct device_node *node); +void xvip_bpl_scaling_factor(u32 fourcc, u32 *numerator, u32 *denominator); +void xvip_width_padding_factor(u32 fourcc, u32 *numerator, u32 *denominator); void xvip_set_format_size(struct v4l2_mbus_framefmt *format, const struct v4l2_subdev_format *fmt); int xvip_enum_mbus_code(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index edce0402155d..7890cc11632f 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -27,16 +27,27 @@ #define XVIPP_DMA_S2MM 0 #define XVIPP_DMA_MM2S 1 +/* + * This is for backward compatibility for existing applications, + * and planned to be deprecated + */ +static bool xvip_is_mplane = true; +MODULE_PARM_DESC(is_mplane, + "v4l2 device capability to handle multi planar formats"); +module_param_named(is_mplane, xvip_is_mplane, bool, 0444); + /** * struct xvip_graph_entity - Entity in the video graph * @asd: subdev asynchronous registration information * @entity: media entity, from the corresponding V4L2 subdev * @subdev: V4L2 subdev + * @streaming: status of the V4L2 subdev if streaming or not */ struct xvip_graph_entity { struct v4l2_async_subdev asd; /* must be first */ struct media_entity *entity; struct v4l2_subdev *subdev; + bool streaming; }; static inline struct xvip_graph_entity * @@ -182,6 +193,38 @@ xvip_graph_find_dma(struct xvip_composite_device *xdev, unsigned int port) return NULL; } +/** + * xvip_subdev_set_streaming - Find and update streaming status of subdev + * @xdev: Composite video device + * @subdev: V4L2 sub-device + * @enable: enable/disable streaming status + * + * Walk the xvip graph entities list and find if subdev is present. Returns + * streaming status of subdev and update the status as requested + * + * Return: streaming status (true or false) if successful or warn_on if subdev + * is not present and return false + */ +bool xvip_subdev_set_streaming(struct xvip_composite_device *xdev, + struct v4l2_subdev *subdev, bool enable) +{ + struct xvip_graph_entity *entity; + struct v4l2_async_subdev *asd; + + list_for_each_entry(asd, &xdev->notifier.asd_list, asd_list) { + entity = to_xvip_entity(asd); + if (entity->asd.match.fwnode == of_fwnode_handle(subdev->dev->of_node)) { + bool status = entity->streaming; + + entity->streaming = enable; + return status; + } + } + + WARN(1, "Should never get here\n"); + return false; +} + static int xvip_graph_build_dma(struct xvip_composite_device *xdev) { u32 link_flags = MEDIA_LNK_FL_ENABLED; @@ -276,7 +319,6 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev) } } - of_node_put(ep); return ret; } @@ -442,9 +484,11 @@ static int xvip_graph_dma_init_one(struct xvip_composite_device *xdev, return ret; if (strcmp(direction, "input") == 0) - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + type = xvip_is_mplane ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE; else if (strcmp(direction, "output") == 0) - type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + type = xvip_is_mplane ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : + V4L2_BUF_TYPE_VIDEO_OUTPUT; else return -EINVAL; @@ -462,8 +506,14 @@ static int xvip_graph_dma_init_one(struct xvip_composite_device *xdev, list_add_tail(&dma->list, &xdev->dmas); - xdev->v4l2_caps |= type == V4L2_BUF_TYPE_VIDEO_CAPTURE - ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_VIDEO_OUTPUT; + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + xdev->v4l2_caps |= V4L2_CAP_VIDEO_CAPTURE_MPLANE; + else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + xdev->v4l2_caps |= V4L2_CAP_VIDEO_CAPTURE; + else if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + xdev->v4l2_caps |= V4L2_CAP_VIDEO_OUTPUT; + else if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + xdev->v4l2_caps |= V4L2_CAP_VIDEO_OUTPUT_MPLANE; return 0; } @@ -594,6 +644,7 @@ static int xvip_composite_probe(struct platform_device *pdev) return -ENOMEM; xdev->dev = &pdev->dev; + mutex_init(&xdev->lock); INIT_LIST_HEAD(&xdev->dmas); v4l2_async_notifier_init(&xdev->notifier); @@ -620,6 +671,7 @@ static int xvip_composite_remove(struct platform_device *pdev) { struct xvip_composite_device *xdev = platform_get_drvdata(pdev); + mutex_destroy(&xdev->lock); xvip_graph_cleanup(xdev); xvip_composite_v4l2_cleanup(xdev); diff --git a/drivers/media/platform/xilinx/xilinx-vipp.h b/drivers/media/platform/xilinx/xilinx-vipp.h index e65fce9538f9..24934d57529b 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.h +++ b/drivers/media/platform/xilinx/xilinx-vipp.h @@ -27,6 +27,7 @@ * @notifier: V4L2 asynchronous subdevs notifier * @dmas: list of DMA channels at the pipeline output and input * @v4l2_caps: V4L2 capabilities of the whole device (see VIDIOC_QUERYCAP) + * @lock: This is to ensure all dma path entities acquire same pipeline object */ struct xvip_composite_device { struct v4l2_device v4l2_dev; @@ -37,6 +38,10 @@ struct xvip_composite_device { struct list_head dmas; u32 v4l2_caps; + struct mutex lock; /* lock to protect xvip pipeline instance */ }; +bool xvip_subdev_set_streaming(struct xvip_composite_device *xdev, + struct v4l2_subdev *subdev, bool enable); + #endif /* __XILINX_VIPP_H__ */ diff --git a/drivers/media/platform/xilinx/xilinx-vpss-csc.c b/drivers/media/platform/xilinx/xilinx-vpss-csc.c new file mode 100644 index 000000000000..2c79b6ec1b8b --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vpss-csc.c @@ -0,0 +1,1169 @@ +/* + * Xilinx VPSS Color Space Converter + * + * Copyright (C) 2017 Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/xilinx-v4l2-controls.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "xilinx-vip.h" + +#define XV_CSC_AP_CTRL (0x000) +#define XV_CSC_INVIDEOFORMAT (0x010) +#define XV_CSC_OUTVIDEOFORMAT (0x018) +#define XV_CSC_WIDTH (0x020) +#define XV_CSC_HEIGHT (0x028) +#define XV_CSC_K11 (0x050) +#define XV_CSC_K12 (0x058) +#define XV_CSC_K13 (0x060) +#define XV_CSC_K21 (0x068) +#define XV_CSC_K22 (0x070) +#define XV_CSC_K23 (0x078) +#define XV_CSC_K31 (0x080) +#define XV_CSC_K32 (0x088) +#define XV_CSC_K33 (0x090) +#define XV_CSC_ROFFSET (0x098) +#define XV_CSC_GOFFSET (0x0a0) +#define XV_CSC_BOFFSET (0x0a8) +#define XV_CSC_CLAMPMIN (0x0b0) +#define XV_CSC_CLIPMAX (0x0b8) + +#define XV_CSC_FRACTIONAL_BITS (12) +#define XV_CSC_SCALE_FACTOR (4096) +/* This a VPSS CSC specific macro used to calculate Contrast */ +#define XV_CSC_DIVISOR (10000) +#define XV_CSC_DEFAULT_HEIGHT (720) +#define XV_CSC_DEFAULT_WIDTH (1280) +#define XV_CSC_K_MAX_ROWS (3) +#define XV_CSC_K_MAX_COLUMNS (3) +#define XV_CSC_MIN_WIDTH (64) +#define XV_CSC_MAX_WIDTH (8192) +#define XV_CSC_MIN_HEIGHT (64) +#define XV_CSC_MAX_HEIGHT (4320) + +/* GPIO Reset Assert/De-assert */ +#define XCSC_RESET_ASSERT (1) +#define XCSC_RESET_DEASSERT (0) +/* Streaming Macros */ +#define XCSC_CLAMP_MIN_ZERO (0) +#define XCSC_AP_START BIT(0) +#define XCSC_AP_AUTO_RESTART BIT(7) +#define XCSC_STREAM_ON (XCSC_AP_START | XCSC_AP_AUTO_RESTART) +/* Color Control Macros */ +#define XCSC_COLOR_CTRL_COUNT (5) +#define XCSC_COLOR_CTRL_DEFAULT (50) + +enum xcsc_color_fmt { + XVIDC_CSF_RGB = 0, + XVIDC_CSF_YCRCB_444, + XVIDC_CSF_YCRCB_422, + XVIDC_CSF_YCRCB_420, +}; + +enum xcsc_output_range { + XVIDC_CR_0_255 = 1, + XVIDC_CR_16_240, + XVIDC_CR_16_235 +}; + +enum xcsc_color_depth { + XVIDC_BPC_8 = 8, + XVIDC_BPC_10 = 10, +}; + +static const s32 +rgb_unity_matrix[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1] = { + {XV_CSC_SCALE_FACTOR, 0, 0, 0}, + {0, XV_CSC_SCALE_FACTOR, 0, 0}, + {0, 0, XV_CSC_SCALE_FACTOR, 0}, +}; + +static const s32 +ycrcb_to_rgb_unity[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1] = { + { + 11644 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + 0, + 17927 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + 0 + }, + { + 11644 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + -2132 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + -5329 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + 0 + }, + { + 11644 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + 21124 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + 0, + 0 + }, +}; + +static const s32 +rgb_to_ycrcb_unity[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1] = { + { + 1826 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + 6142 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + 620 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + 0 + }, + { + -1006 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + -3386 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + 4392 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + 0 + }, + { + 4392 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + -3989 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + -403 * XV_CSC_SCALE_FACTOR / XV_CSC_DIVISOR, + 0, + }, +}; + +/** + * struct xcsc_dev - xilinx vpss csc device structure + * @xvip: Xilinx Video IP core struct + * @pads: Media bus pads for VPSS CSC + * @formats: Current media bus formats + * @default_formats: Default media bus formats for VPSS CSC + * @vip_formats: Pointer to DT specified media bus code info + * @ctrl_handler: V4L2 Control Handler struct + * @custom_ctrls: Array of pointers to various custom controls + * @cft_in: IP or Hardware specific input video format + * @cft_out: IP or Hardware specific output video format + * @output_range: Color range for Outgoing video + * @color_depth: Data width used to represent color + * @brightness: Expected brightness value + * @contrast: Expected contrast value + * @red_gain: Expected red gain + * @green_gain: Expect green gain + * @blue_gain: Expected blue gain + * @brightness_active: Current brightness value + * @contrast_active: Current contrast value + * @red_gain_active: Current red gain + * @green_gain_active: Current green gain + * @blue_gain_active: Current blue gain + * @k_hw : Coefficients to be written to IP/Hardware + * @shadow_coeff: Coefficients to track RGB equivalents for color controls + * @clip_max: Maximum value to clip output color range + * @rst_gpio: Handle to PS GPIO specifier to assert/de-assert the reset line + * @max_width: Maximum width supported by IP. + * @max_height: Maximum height supported by IP. + */ +struct xcsc_dev { + struct xvip_device xvip; + struct media_pad pads[2]; + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct xvip_video_format *vip_formats[2]; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *custom_ctrls[XCSC_COLOR_CTRL_COUNT]; + + enum xcsc_color_fmt cft_in; + enum xcsc_color_fmt cft_out; + enum xcsc_output_range output_range; + enum xcsc_color_depth color_depth; + s32 brightness; + s32 contrast; + s32 red_gain; + s32 green_gain; + s32 blue_gain; + s32 brightness_active; + s32 contrast_active; + s32 red_gain_active; + s32 green_gain_active; + s32 blue_gain_active; + s32 k_hw[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1]; + s32 shadow_coeff[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1]; + s32 clip_max; + struct gpio_desc *rst_gpio; + u32 max_width; + u32 max_height; +}; + +#ifdef DEBUG +static u32 xcsc_read(struct xcsc_dev *xcsc, u32 reg) +{ + u32 data; + + data = xvip_read(&xcsc->xvip, reg); + return data; +} + +static void xcsc_get_coeff(struct xcsc_dev *xcsc, s32 C[3][4]) +{ + C[0][0] = xcsc_read(xcsc, XV_CSC_K11); + C[0][1] = xcsc_read(xcsc, XV_CSC_K12); + C[0][2] = xcsc_read(xcsc, XV_CSC_K13); + C[1][0] = xcsc_read(xcsc, XV_CSC_K21); + C[1][1] = xcsc_read(xcsc, XV_CSC_K22); + C[1][2] = xcsc_read(xcsc, XV_CSC_K23); + C[2][0] = xcsc_read(xcsc, XV_CSC_K31); + C[2][1] = xcsc_read(xcsc, XV_CSC_K32); + C[2][2] = xcsc_read(xcsc, XV_CSC_K33); + C[0][3] = xcsc_read(xcsc, XV_CSC_ROFFSET); + C[1][3] = xcsc_read(xcsc, XV_CSC_GOFFSET); + C[2][3] = xcsc_read(xcsc, XV_CSC_BOFFSET); +} + +static void xcsc_print_coeff(struct xcsc_dev *xcsc) +{ + s32 C[3][4]; + + xcsc_get_coeff(xcsc, C); + + dev_info(xcsc->xvip.dev, + "-------------CSC Coeff Dump Start------\n"); + dev_info(xcsc->xvip.dev, + " R row : %5d %5d %5d\n", + (s16)C[0][0], (s16)C[0][1], (s16)C[0][2]); + dev_info(xcsc->xvip.dev, + " G row : %5d %5d %5d\n", + (s16)C[1][0], (s16)C[1][1], (s16)C[1][2]); + dev_info(xcsc->xvip.dev, + " B row : %5d %5d %5d\n", + (s16)C[2][0], (s16)C[2][1], (s16)C[2][2]); + dev_info(xcsc->xvip.dev, + "Offset : %5d %5d %5d\n", + (s16)C[0][3], (s16)C[1][3], (s16)C[2][3]); + dev_info(xcsc->xvip.dev, + "ClampMin: %3d ClipMax %3d", + xcsc_read(xcsc, XV_CSC_CLAMPMIN), + xcsc_read(xcsc, XV_CSC_CLIPMAX)); + dev_info(xcsc->xvip.dev, + "-------------CSC Coeff Dump Stop-------\n"); +} + +static void +xcsc_log_coeff(struct device *dev, + s32 coeff[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1]) +{ + if (!dev) + return; + dev_dbg(dev, "--- %s : Start Coeff Log ---", __func__); + dev_dbg(dev, "R row : %5d %5d %5d\n", + coeff[0][0], coeff[0][1], coeff[0][2]); + dev_dbg(dev, "G row : %5d %5d %5d\n", + coeff[1][0], coeff[1][1], coeff[1][2]); + dev_dbg(dev, "B row : %5d %5d %5d\n", + coeff[2][0], coeff[2][1], coeff[2][2]); + dev_dbg(dev, "Offset: %5d %5d %5d\n", + coeff[0][3], coeff[1][3], coeff[2][3]); + dev_dbg(dev, "--- %s : Stop Coeff Log ---", __func__); +} + +static void xcsc_print_k_hw(struct xcsc_dev *xcsc) +{ + dev_dbg(xcsc->xvip.dev, + "-------------CSC Driver k_hw[][] Dump------------\n"); + xcsc_log_coeff(xcsc->xvip.dev, xcsc->k_hw); + dev_dbg(xcsc->xvip.dev, + "-------------------------------------------------\n"); +} +#endif /* DEBUG */ + +static void xcsc_write(struct xcsc_dev *xcsc, u32 reg, u32 data) +{ + xvip_write(&xcsc->xvip, reg, data); +} + +static void xcsc_write_rgb_3x3(struct xcsc_dev *xcsc) +{ + /* Write Matrix Coefficients */ + xcsc_write(xcsc, XV_CSC_K11, xcsc->k_hw[0][0]); + xcsc_write(xcsc, XV_CSC_K12, xcsc->k_hw[0][1]); + xcsc_write(xcsc, XV_CSC_K13, xcsc->k_hw[0][2]); + xcsc_write(xcsc, XV_CSC_K21, xcsc->k_hw[1][0]); + xcsc_write(xcsc, XV_CSC_K22, xcsc->k_hw[1][1]); + xcsc_write(xcsc, XV_CSC_K23, xcsc->k_hw[1][2]); + xcsc_write(xcsc, XV_CSC_K31, xcsc->k_hw[2][0]); + xcsc_write(xcsc, XV_CSC_K32, xcsc->k_hw[2][1]); + xcsc_write(xcsc, XV_CSC_K33, xcsc->k_hw[2][2]); +} + +static void xcsc_write_rgb_offset(struct xcsc_dev *xcsc) +{ + /* Write RGB Offsets */ + xcsc_write(xcsc, XV_CSC_ROFFSET, xcsc->k_hw[0][3]); + xcsc_write(xcsc, XV_CSC_GOFFSET, xcsc->k_hw[1][3]); + xcsc_write(xcsc, XV_CSC_BOFFSET, xcsc->k_hw[2][3]); +} + +static void xcsc_write_coeff(struct xcsc_dev *xcsc) +{ + xcsc_write_rgb_3x3(xcsc); + xcsc_write_rgb_offset(xcsc); +} + +static void xcsc_set_v4l2_ctrl_defaults(struct xcsc_dev *xcsc) +{ + unsigned int i; + + mutex_lock(xcsc->ctrl_handler.lock); + for (i = 0; i < XCSC_COLOR_CTRL_COUNT; i++) + xcsc->custom_ctrls[i]->cur.val = XCSC_COLOR_CTRL_DEFAULT; + mutex_unlock(xcsc->ctrl_handler.lock); +} + +static void xcsc_set_control_defaults(struct xcsc_dev *xcsc) +{ + /* These are VPSS CSC IP specific defaults */ + xcsc->brightness = 120; + xcsc->contrast = 0; + xcsc->red_gain = 120; + xcsc->blue_gain = 120; + xcsc->green_gain = 120; + xcsc->brightness_active = 120; + xcsc->contrast_active = 0; + xcsc->red_gain_active = 120; + xcsc->blue_gain_active = 120; + xcsc->green_gain_active = 120; +} + +static void xcsc_copy_coeff( + s32 dest[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1], + s32 const src[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1]) +{ + unsigned int i, j; + + for (i = 0; i < XV_CSC_K_MAX_ROWS; i++) + for (j = 0; j < XV_CSC_K_MAX_COLUMNS + 1; j++) + memcpy(&dest[i][j], &src[i][j], sizeof(dest[0][0])); +} + +static void xcsc_set_unity_matrix(struct xcsc_dev *xcsc) +{ + xcsc_copy_coeff(xcsc->k_hw, rgb_unity_matrix); + xcsc_copy_coeff(xcsc->shadow_coeff, rgb_unity_matrix); +} + +static void xcsc_set_default_state(struct xcsc_dev *xcsc) +{ + xcsc->cft_in = XVIDC_CSF_RGB; + xcsc->cft_out = XVIDC_CSF_RGB; + xcsc->output_range = XVIDC_CR_0_255; + /* Needed to add 10, 12 and 16 bit color depth support */ + xcsc->clip_max = BIT(xcsc->color_depth) - 1; + xcsc_set_control_defaults(xcsc); + xcsc_set_unity_matrix(xcsc); + xcsc_write(xcsc, XV_CSC_INVIDEOFORMAT, xcsc->cft_in); + xcsc_write(xcsc, XV_CSC_OUTVIDEOFORMAT, xcsc->cft_out); + xcsc_write_coeff(xcsc); + xcsc_write(xcsc, XV_CSC_CLIPMAX, xcsc->clip_max); + xcsc_write(xcsc, XV_CSC_CLAMPMIN, XCSC_CLAMP_MIN_ZERO); +} + +static void +xcsc_ycrcb_to_rgb(struct xcsc_dev *xcsc, s32 *clip_max, + s32 temp[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1]) +{ + u16 bpc_scale = BIT(xcsc->color_depth - 8); + + /* + * See http://graficaobscura.com/matrix/index.html for + * how these numbers are derived. The VPSS CSC IP is + * derived from this Matrix style algorithm. And the + * 'magic' numbers here are derived from the algorithm. + * + * XV_CSC_DIVISOR is used to help with floating constants + * while performing multiplicative operations + * + * Coefficients valid only for BT 709 + */ + dev_dbg(xcsc->xvip.dev, "Performing YCrCb to RGB BT 709"); + xcsc_copy_coeff(temp, ycrcb_to_rgb_unity); + temp[0][3] = -248 * bpc_scale; + temp[1][3] = 77 * bpc_scale; + temp[2][3] = -289 * bpc_scale; + *clip_max = BIT(xcsc->color_depth) - 1; +} + +static void +xcsc_matrix_multiply(s32 K1[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1], + s32 K2[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1], + s32 kout[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1]) +{ + s32 A, B, C, D, E, F, G, H, I, J, K, L, M, N; + s32 O, P, Q, R, S, T, U, V, W, X; + + A = K1[0][0]; B = K1[0][1]; C = K1[0][2]; J = K1[0][3]; + D = K1[1][0]; E = K1[1][1]; F = K1[1][2]; K = K1[1][3]; + G = K1[2][0]; H = K1[2][1]; I = K1[2][2]; L = K1[2][3]; + + M = K2[0][0]; N = K2[0][1]; O = K2[0][2]; V = K2[0][3]; + P = K2[1][0]; Q = K2[1][1]; R = K2[1][2]; W = K2[1][3]; + S = K2[2][0]; T = K2[2][1]; U = K2[2][2]; X = K2[2][3]; + + kout[0][0] = (M * A + N * D + O * G) / XV_CSC_SCALE_FACTOR; + kout[0][1] = (M * B + N * E + O * H) / XV_CSC_SCALE_FACTOR; + kout[0][2] = (M * C + N * F + O * I) / XV_CSC_SCALE_FACTOR; + kout[1][0] = (P * A + Q * D + R * G) / XV_CSC_SCALE_FACTOR; + kout[1][1] = (P * B + Q * E + R * H) / XV_CSC_SCALE_FACTOR; + kout[1][2] = (P * C + Q * F + R * I) / XV_CSC_SCALE_FACTOR; + kout[2][0] = (S * A + T * D + U * G) / XV_CSC_SCALE_FACTOR; + kout[2][1] = (S * B + T * E + U * H) / XV_CSC_SCALE_FACTOR; + kout[2][2] = (S * C + T * F + U * I) / XV_CSC_SCALE_FACTOR; + kout[0][3] = ((M * J + N * K + O * L) / XV_CSC_SCALE_FACTOR) + V; + kout[1][3] = ((P * J + Q * K + R * L) / XV_CSC_SCALE_FACTOR) + W; + kout[2][3] = ((S * J + T * K + U * L) / XV_CSC_SCALE_FACTOR) + X; +} + +static void +xcsc_rgb_to_ycrcb(struct xcsc_dev *xcsc, s32 *clip_max, + s32 temp[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1]) +{ + u16 bpc_scale = BIT(xcsc->color_depth - 8); + + /* + * See http://graficaobscura.com/matrix/index.html for + * how these numbers are derived. The VPSS CSC IP is + * derived from this Matrix style algorithm. And the + * 'magic' numbers here are derived from the algorithm. + * + * XV_CSC_DIVISOR is used to help with floating constants + * while performing multiplicative operations + * + * Coefficients valid only for BT 709 + */ + dev_dbg(xcsc->xvip.dev, "Performing RGB to YCrCb BT 709"); + xcsc_copy_coeff(temp, rgb_to_ycrcb_unity); + temp[0][3] = 16 * bpc_scale; + temp[1][3] = 128 * bpc_scale; + temp[2][3] = 128 * bpc_scale; + *clip_max = BIT(xcsc->color_depth) - 1; +} + +static int xcsc_update_formats(struct xcsc_dev *xcsc) +{ + u32 color_in, color_out; + + /* Write In and Out Video Formats */ + color_in = xcsc->formats[XVIP_PAD_SINK].code; + color_out = xcsc->formats[XVIP_PAD_SOURCE].code; + + switch (color_in) { + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RBG101010_1X30: + dev_dbg(xcsc->xvip.dev, "Media Format In : RGB"); + xcsc->cft_in = XVIDC_CSF_RGB; + break; + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_VUY10_1X30: + dev_dbg(xcsc->xvip.dev, "Media Format In : YUV 444"); + xcsc->cft_in = XVIDC_CSF_YCRCB_444; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + dev_dbg(xcsc->xvip.dev, "Media Format In : YUV 422"); + xcsc->cft_in = XVIDC_CSF_YCRCB_422; + break; + case MEDIA_BUS_FMT_VYYUYY8_1X24: + case MEDIA_BUS_FMT_VYYUYY10_4X20: + dev_dbg(xcsc->xvip.dev, "Media Format In : YUV 420"); + xcsc->cft_in = XVIDC_CSF_YCRCB_420; + break; + } + + switch (color_out) { + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RBG101010_1X30: + xcsc->cft_out = XVIDC_CSF_RGB; + dev_dbg(xcsc->xvip.dev, "Media Format Out : RGB"); + if (color_in != MEDIA_BUS_FMT_RBG888_1X24) + xcsc_ycrcb_to_rgb(xcsc, &xcsc->clip_max, xcsc->k_hw); + else + xcsc_set_unity_matrix(xcsc); + break; + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_VUY10_1X30: + xcsc->cft_out = XVIDC_CSF_YCRCB_444; + dev_dbg(xcsc->xvip.dev, "Media Format Out : YUV 444"); + if (color_in == MEDIA_BUS_FMT_RBG888_1X24) + xcsc_rgb_to_ycrcb(xcsc, &xcsc->clip_max, xcsc->k_hw); + else + xcsc_set_unity_matrix(xcsc); + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + xcsc->cft_out = XVIDC_CSF_YCRCB_422; + dev_dbg(xcsc->xvip.dev, "Media Format Out : YUV 422"); + if (color_in == MEDIA_BUS_FMT_RBG888_1X24) + xcsc_rgb_to_ycrcb(xcsc, &xcsc->clip_max, xcsc->k_hw); + else + xcsc_set_unity_matrix(xcsc); + break; + case MEDIA_BUS_FMT_VYYUYY8_1X24: + case MEDIA_BUS_FMT_VYYUYY10_4X20: + xcsc->cft_out = XVIDC_CSF_YCRCB_420; + dev_dbg(xcsc->xvip.dev, "Media Format Out : YUV 420"); + if (color_in == MEDIA_BUS_FMT_RBG888_1X24) + xcsc_rgb_to_ycrcb(xcsc, &xcsc->clip_max, xcsc->k_hw); + else + xcsc_set_unity_matrix(xcsc); + break; + } + + xcsc_write(xcsc, XV_CSC_INVIDEOFORMAT, xcsc->cft_in); + xcsc_write(xcsc, XV_CSC_OUTVIDEOFORMAT, xcsc->cft_out); + + xcsc_write_coeff(xcsc); + + xcsc_write(xcsc, XV_CSC_CLIPMAX, xcsc->clip_max); + xcsc_write(xcsc, XV_CSC_CLAMPMIN, XCSC_CLAMP_MIN_ZERO); +#ifdef DEBUG + xcsc_print_k_hw(xcsc); + xcsc_print_coeff(xcsc); +#endif + return 0; +} + +static inline struct xcsc_dev *to_csc(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xcsc_dev, xvip.subdev); +} + +static struct v4l2_mbus_framefmt * +__xcsc_get_pad_format(struct xcsc_dev *xcsc, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xcsc->xvip.subdev, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xcsc->formats[pad]; + default: + return NULL; + } +} + +static void +xcsc_correct_coeff(struct xcsc_dev *xcsc, + s32 temp[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1]) +{ + s32 csc_change[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1] = { {0} }; + s32 csc_extra[XV_CSC_K_MAX_ROWS][XV_CSC_K_MAX_COLUMNS + 1] = { {0} }; + u32 mbus_in = xcsc->formats[XVIP_PAD_SINK].code; + u32 mbus_out = xcsc->formats[XVIP_PAD_SOURCE].code; + +#ifdef DEBUG + xcsc_log_coeff(xcsc->xvip.dev, temp); +#endif + if (mbus_in == MEDIA_BUS_FMT_RBG888_1X24 && mbus_out == mbus_in) { + dev_dbg(xcsc->xvip.dev, "%s : RGB to RGB", __func__); + xcsc_copy_coeff(xcsc->k_hw, + (const s32 (*)[XV_CSC_K_MAX_COLUMNS + 1])temp); + } else if (mbus_in == MEDIA_BUS_FMT_RBG888_1X24 && + mbus_out != MEDIA_BUS_FMT_RBG888_1X24) { + dev_dbg(xcsc->xvip.dev, "%s : RGB to YUV", __func__); + xcsc_rgb_to_ycrcb(xcsc, &xcsc->clip_max, csc_change); + xcsc_matrix_multiply(temp, csc_change, xcsc->k_hw); + } else if (mbus_in != MEDIA_BUS_FMT_RBG888_1X24 && + mbus_out == MEDIA_BUS_FMT_RBG888_1X24) { + dev_dbg(xcsc->xvip.dev, "%s : YUV to RGB", __func__); + xcsc_ycrcb_to_rgb(xcsc, &xcsc->clip_max, csc_change); + xcsc_matrix_multiply(csc_change, temp, xcsc->k_hw); + } else if (mbus_in != MEDIA_BUS_FMT_RBG888_1X24 && + mbus_out != MEDIA_BUS_FMT_RBG888_1X24) { + dev_dbg(xcsc->xvip.dev, "%s : YUV to YUV", __func__); + xcsc_ycrcb_to_rgb(xcsc, &xcsc->clip_max, csc_change); + xcsc_matrix_multiply(csc_change, temp, csc_extra); + xcsc_rgb_to_ycrcb(xcsc, &xcsc->clip_max, csc_change); + xcsc_matrix_multiply(csc_extra, csc_change, xcsc->k_hw); + } else { + /* Should never get here */ + WARN_ON(1); + } +} + +static void xcsc_set_brightness(struct xcsc_dev *xcsc) +{ + unsigned int i, j; + + dev_dbg(xcsc->xvip.dev, + "%s : Brightness %d Brightness Active %d", + __func__, + ((xcsc->brightness - 20) / 2), + ((xcsc->brightness_active - 20) / 2)); + if (xcsc->brightness == xcsc->brightness_active) + return; + for (i = 0; i < XV_CSC_K_MAX_ROWS; i++) { + for (j = 0; j < XV_CSC_K_MAX_COLUMNS; j++) { + xcsc->shadow_coeff[i][j] = (xcsc->shadow_coeff[i][j] * + xcsc->brightness) / + xcsc->brightness_active; + } + } + xcsc->brightness_active = xcsc->brightness; + xcsc_correct_coeff(xcsc, xcsc->shadow_coeff); + xcsc_write_coeff(xcsc); +} + +static void xcsc_set_contrast(struct xcsc_dev *xcsc) +{ + s32 contrast; + u8 scale = BIT(xcsc->color_depth - 8); + + contrast = xcsc->contrast - xcsc->contrast_active; + dev_dbg(xcsc->xvip.dev, + "%s : Contrast Difference %d scale = %d", + __func__, contrast, scale); + /* Avoid updates if same */ + if (!contrast) + return; + /* Update RGB Offsets */ + xcsc->shadow_coeff[0][3] += contrast * scale; + xcsc->shadow_coeff[1][3] += contrast * scale; + xcsc->shadow_coeff[2][3] += contrast * scale; + xcsc->contrast_active = xcsc->contrast; + xcsc_correct_coeff(xcsc, xcsc->shadow_coeff); + xcsc_write_coeff(xcsc); +} + +static void xcsc_set_red_gain(struct xcsc_dev *xcsc) +{ + dev_dbg(xcsc->xvip.dev, + "%s: Red Gain %d Red Gain Active %d", __func__, + (xcsc->red_gain - 20) / 2, + (xcsc->red_gain_active - 20) / 2); + + if (xcsc->red_gain != xcsc->red_gain_active) { + xcsc->shadow_coeff[0][0] = (xcsc->shadow_coeff[0][0] * + xcsc->red_gain) / + xcsc->red_gain_active; + xcsc->shadow_coeff[0][1] = (xcsc->shadow_coeff[0][1] * + xcsc->red_gain) / + xcsc->red_gain_active; + xcsc->shadow_coeff[0][2] = (xcsc->shadow_coeff[0][2] * + xcsc->red_gain) / + xcsc->red_gain_active; + xcsc->red_gain_active = xcsc->red_gain; + xcsc_correct_coeff(xcsc, xcsc->shadow_coeff); + xcsc_write_coeff(xcsc); + } +} + +static void xcsc_set_green_gain(struct xcsc_dev *xcsc) +{ + dev_dbg(xcsc->xvip.dev, + "%s: Green Gain %d Green Gain Active %d", __func__, + (xcsc->green_gain - 20) / 2, + (xcsc->green_gain_active - 20) / 2); + + if (xcsc->green_gain != xcsc->green_gain_active) { + xcsc->shadow_coeff[1][0] = (xcsc->shadow_coeff[1][0] * + xcsc->green_gain) / + xcsc->green_gain_active; + xcsc->shadow_coeff[1][1] = (xcsc->shadow_coeff[1][1] * + xcsc->green_gain) / + xcsc->green_gain_active; + xcsc->shadow_coeff[1][2] = (xcsc->shadow_coeff[1][2] * + xcsc->green_gain) / + xcsc->green_gain_active; + xcsc->green_gain_active = xcsc->green_gain; + xcsc_correct_coeff(xcsc, xcsc->shadow_coeff); + xcsc_write_coeff(xcsc); + } +} + +static void xcsc_set_blue_gain(struct xcsc_dev *xcsc) +{ + dev_dbg(xcsc->xvip.dev, + "%s: Blue Gain %d Blue Gain Active %d", __func__, + (xcsc->blue_gain - 20) / 2, + (xcsc->blue_gain_active - 20) / 2); + + if (xcsc->blue_gain != xcsc->blue_gain_active) { + xcsc->shadow_coeff[2][0] = (xcsc->shadow_coeff[2][0] * + xcsc->blue_gain) / + xcsc->blue_gain_active; + xcsc->shadow_coeff[2][1] = (xcsc->shadow_coeff[2][1] * + xcsc->blue_gain) / + xcsc->blue_gain_active; + xcsc->shadow_coeff[2][2] = (xcsc->shadow_coeff[2][2] * + xcsc->blue_gain) / + xcsc->blue_gain_active; + xcsc->blue_gain_active = xcsc->blue_gain; + xcsc_correct_coeff(xcsc, xcsc->shadow_coeff); + xcsc_write_coeff(xcsc); + } +} + +static void xcsc_set_size(struct xcsc_dev *xcsc) +{ + u32 width, height; + + width = xcsc->formats[XVIP_PAD_SINK].width; + height = xcsc->formats[XVIP_PAD_SINK].height; + dev_dbg(xcsc->xvip.dev, "%s : Setting width %d and height %d", + __func__, width, height); + xcsc_write(xcsc, XV_CSC_WIDTH, width); + xcsc_write(xcsc, XV_CSC_HEIGHT, height); +} + +static int xcsc_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xcsc_dev *xcsc = to_csc(subdev); + + dev_dbg(xcsc->xvip.dev, "%s : Stream %s", __func__, + enable ? "On" : "Off"); + if (!enable) { + /* Reset the Global IP Reset through PS GPIO */ + gpiod_set_value_cansleep(xcsc->rst_gpio, XCSC_RESET_ASSERT); + gpiod_set_value_cansleep(xcsc->rst_gpio, XCSC_RESET_DEASSERT); + return 0; + } + xcsc_write(xcsc, XV_CSC_INVIDEOFORMAT, xcsc->cft_in); + xcsc_write(xcsc, XV_CSC_OUTVIDEOFORMAT, xcsc->cft_out); + xcsc_write(xcsc, XV_CSC_CLIPMAX, xcsc->clip_max); + xcsc_write(xcsc, XV_CSC_CLAMPMIN, XCSC_CLAMP_MIN_ZERO); + xcsc_set_size(xcsc); + xcsc_write_coeff(xcsc); +#ifdef DEBUG + xcsc_print_coeff(xcsc); + dev_dbg(xcsc->xvip.dev, "cft_in = %d cft_out = %d", + xcsc_read(xcsc, XV_CSC_INVIDEOFORMAT), + xcsc_read(xcsc, XV_CSC_OUTVIDEOFORMAT)); + dev_dbg(xcsc->xvip.dev, "clipmax = %d clampmin = %d", + xcsc_read(xcsc, XV_CSC_CLIPMAX), + xcsc_read(xcsc, XV_CSC_CLAMPMIN)); + dev_dbg(xcsc->xvip.dev, "height = %d width = %d", + xcsc_read(xcsc, XV_CSC_HEIGHT), + xcsc_read(xcsc, XV_CSC_WIDTH)); +#endif + /* Start VPSS CSC IP */ + xcsc_write(xcsc, XV_CSC_AP_CTRL, XCSC_STREAM_ON); + return 0; +} + +static const struct v4l2_subdev_video_ops xcsc_video_ops = { + .s_stream = xcsc_s_stream, +}; + +static int xcsc_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcsc_dev *xcsc = to_csc(subdev); + + fmt->format = *__xcsc_get_pad_format(xcsc, cfg, fmt->pad, fmt->which); + return 0; +} + +static int xcsc_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xcsc_dev *xcsc = to_csc(subdev); + struct v4l2_mbus_framefmt *__format; + struct v4l2_mbus_framefmt *__propagate; + + __format = __xcsc_get_pad_format(xcsc, cfg, fmt->pad, fmt->which); + /* Propagate to Source Pad */ + __propagate = __xcsc_get_pad_format(xcsc, cfg, + XVIP_PAD_SOURCE, fmt->which); + *__format = fmt->format; + + __format->width = clamp_t(unsigned int, fmt->format.width, + XV_CSC_MIN_WIDTH, xcsc->max_width); + __format->height = clamp_t(unsigned int, fmt->format.height, + XV_CSC_MIN_HEIGHT, xcsc->max_height); + + switch (__format->code) { + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYYUYY8_1X24: + case MEDIA_BUS_FMT_VYYUYY10_4X20: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_VUY10_1X30: + break; + default: + /* Unsupported Format. Default to RGB */ + __format->code = MEDIA_BUS_FMT_RBG888_1X24; + return -EINVAL; + } + + /* Always propagate Sink image size to Source */ + __propagate->width = __format->width; + __propagate->height = __format->height; + + fmt->format = *__format; + xcsc_update_formats(xcsc); + xcsc_set_control_defaults(xcsc); + xcsc_set_v4l2_ctrl_defaults(xcsc); + dev_info(xcsc->xvip.dev, "VPSS CSC color controls reset to defaults"); + return 0; +} + +static const struct v4l2_subdev_pad_ops xcsc_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xvip_enum_frame_size, + .get_fmt = xcsc_get_format, + .set_fmt = xcsc_set_format, +}; + +static const struct v4l2_subdev_ops xcsc_ops = { + .video = &xcsc_video_ops, + .pad = &xcsc_pad_ops +}; + +static int xcsc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct xcsc_dev *xcsc = container_of(ctrl->handler, + struct xcsc_dev, + ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_XILINX_CSC_BRIGHTNESS: + xcsc->brightness = (2 * ctrl->val) + 20; + xcsc_set_brightness(xcsc); + break; + case V4L2_CID_XILINX_CSC_CONTRAST: + xcsc->contrast = (4 * ctrl->val) - 200; + xcsc_set_contrast(xcsc); + break; + case V4L2_CID_XILINX_CSC_RED_GAIN: + xcsc->red_gain = (2 * ctrl->val) + 20; + xcsc_set_red_gain(xcsc); + break; + case V4L2_CID_XILINX_CSC_BLUE_GAIN: + xcsc->blue_gain = (2 * ctrl->val) + 20; + xcsc_set_blue_gain(xcsc); + break; + case V4L2_CID_XILINX_CSC_GREEN_GAIN: + xcsc->green_gain = (2 * ctrl->val) + 20; + xcsc_set_green_gain(xcsc); + break; + } +#ifdef DEBUG + xcsc_print_k_hw(xcsc); + xcsc_print_coeff(xcsc); +#endif + return 0; +} + +static const struct v4l2_ctrl_ops xcsc_ctrl_ops = { + .s_ctrl = xcsc_s_ctrl, +}; + +static struct v4l2_ctrl_config xcsc_color_ctrls[XCSC_COLOR_CTRL_COUNT] = { + /* Brightness */ + { + .ops = &xcsc_ctrl_ops, + .id = V4L2_CID_XILINX_CSC_BRIGHTNESS, + .name = "CSC Brightness", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 100, + .step = 1, + .def = XCSC_COLOR_CTRL_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, + /* Contrast */ + { + .ops = &xcsc_ctrl_ops, + .id = V4L2_CID_XILINX_CSC_CONTRAST, + .name = "CSC Contrast", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 100, + .step = 1, + .def = XCSC_COLOR_CTRL_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, + /* Red Gain */ + { + .ops = &xcsc_ctrl_ops, + .id = V4L2_CID_XILINX_CSC_RED_GAIN, + .name = "CSC Red Gain", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 100, + .step = 1, + .def = XCSC_COLOR_CTRL_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, + /* Blue Gain */ + { + .ops = &xcsc_ctrl_ops, + .id = V4L2_CID_XILINX_CSC_BLUE_GAIN, + .name = "CSC Blue Gain", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 100, + .step = 1, + .def = XCSC_COLOR_CTRL_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, + /* Green Gain */ + { + .ops = &xcsc_ctrl_ops, + .id = V4L2_CID_XILINX_CSC_GREEN_GAIN, + .name = "CSC Green Gain", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 100, + .step = 1, + .def = XCSC_COLOR_CTRL_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, +}; + +static int xcsc_open(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + struct xcsc_dev *xcsc = to_csc(subdev); + struct v4l2_mbus_framefmt *format; + + /* Initialize with default formats */ + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK); + *format = xcsc->default_formats[XVIP_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE); + *format = xcsc->default_formats[XVIP_PAD_SOURCE]; + + return 0; +} + +static int xcsc_close(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static const struct v4l2_subdev_internal_ops xcsc_internal_ops = { + .open = xcsc_open, + .close = xcsc_close, +}; + +static const struct media_entity_operations xcsc_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int xcsc_parse_of(struct xcsc_dev *xcsc) +{ + struct device *dev = xcsc->xvip.dev; + struct device_node *node = xcsc->xvip.dev->of_node; + const struct xvip_video_format *vip_format; + struct device_node *ports, *port; + int rval; + u32 port_id = 0; + u32 video_width[2]; + + rval = of_property_read_u32(node, "xlnx,max-height", &xcsc->max_height); + if (rval < 0) { + dev_err(dev, "xlnx,max-height is missing!"); + return -EINVAL; + } else if (xcsc->max_height > XV_CSC_MAX_HEIGHT || + xcsc->max_height < XV_CSC_MIN_HEIGHT) { + dev_err(dev, "Invalid height in dt"); + return -EINVAL; + } + + rval = of_property_read_u32(node, "xlnx,max-width", &xcsc->max_width); + if (rval < 0) { + dev_err(dev, "xlnx,max-width is missing!"); + return -EINVAL; + } else if (xcsc->max_width > XV_CSC_MAX_WIDTH || + xcsc->max_width < XV_CSC_MIN_WIDTH) { + dev_err(dev, "Invalid width in dt"); + return -EINVAL; + } + + ports = of_get_child_by_name(node, "ports"); + if (!ports) + ports = node; + + /* Get the format description for each pad */ + for_each_child_of_node(ports, port) { + if (port->name && (of_node_cmp(port->name, "port") == 0)) { + vip_format = xvip_of_get_format(port); + if (IS_ERR(vip_format)) { + dev_err(dev, "Invalid media pad format in DT"); + return PTR_ERR(vip_format); + } + + rval = of_property_read_u32(port, "reg", &port_id); + if (rval < 0) { + dev_err(dev, "No reg in DT to specify pad"); + return rval; + } + + if (port_id != 0 && port_id != 1) { + dev_err(dev, "Invalid reg in DT"); + return -EINVAL; + } + xcsc->vip_formats[port_id] = vip_format; + + rval = of_property_read_u32(port, "xlnx,video-width", + &video_width[port_id]); + if (rval < 0) { + dev_err(dev, + "DT Port%d xlnx,video-width not found", + port_id); + return rval; + } + } + } + if (video_width[0] != video_width[1]) { + dev_err(dev, "Changing video width in DT not supported"); + return -EINVAL; + } + switch (video_width[0]) { + case XVIDC_BPC_8: + case XVIDC_BPC_10: + xcsc->color_depth = video_width[0]; + break; + default: + dev_err(dev, "Unsupported color depth %d", video_width[0]); + return -EINVAL; + } + /* Reset GPIO */ + xcsc->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(xcsc->rst_gpio)) { + if (PTR_ERR(xcsc->rst_gpio) != -EPROBE_DEFER) + dev_err(dev, "Reset GPIO not setup in DT"); + return PTR_ERR(xcsc->rst_gpio); + } + return 0; +} + +static int xcsc_probe(struct platform_device *pdev) +{ + struct xcsc_dev *xcsc; + struct v4l2_subdev *subdev; + struct v4l2_mbus_framefmt *def_fmt; + int rval, itr; + + xcsc = devm_kzalloc(&pdev->dev, sizeof(*xcsc), GFP_KERNEL); + if (!xcsc) + return -ENOMEM; + + xcsc->xvip.dev = &pdev->dev; + + rval = xcsc_parse_of(xcsc); + if (rval < 0) + return rval; + + /* Reset and initialize the core */ + gpiod_set_value_cansleep(xcsc->rst_gpio, XCSC_RESET_DEASSERT); + rval = xvip_init_resources(&xcsc->xvip); + if (rval < 0) + return rval; + + /* Init v4l2 subdev */ + subdev = &xcsc->xvip.subdev; + v4l2_subdev_init(subdev, &xcsc_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xcsc_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xcsc); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Default Formats Initialization */ + xcsc_set_default_state(xcsc); + def_fmt = &xcsc->default_formats[XVIP_PAD_SINK]; + def_fmt->code = xcsc->vip_formats[XVIP_PAD_SINK]->code; + def_fmt->field = V4L2_FIELD_NONE; + def_fmt->colorspace = V4L2_COLORSPACE_REC709; + def_fmt->width = XV_CSC_DEFAULT_WIDTH; + def_fmt->height = XV_CSC_DEFAULT_HEIGHT; + xcsc->formats[XVIP_PAD_SINK] = *def_fmt; + /* Source supports only YUV 444, YUV 422, and RGB */ + def_fmt = &xcsc->default_formats[XVIP_PAD_SOURCE]; + *def_fmt = xcsc->default_formats[XVIP_PAD_SINK]; + def_fmt->code = xcsc->vip_formats[XVIP_PAD_SOURCE]->code; + def_fmt->width = XV_CSC_DEFAULT_WIDTH; + def_fmt->height = XV_CSC_DEFAULT_HEIGHT; + xcsc->formats[XVIP_PAD_SOURCE] = *def_fmt; + xcsc->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xcsc->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + /* Init Media Entity */ + subdev->entity.ops = &xcsc_media_ops; + rval = media_entity_pads_init(&subdev->entity, 2, xcsc->pads); + if (rval < 0) + goto media_error; + /* V4L2 Control Setup */ + v4l2_ctrl_handler_init(&xcsc->ctrl_handler, + ARRAY_SIZE(xcsc_color_ctrls)); + for (itr = 0; itr < ARRAY_SIZE(xcsc_color_ctrls); itr++) { + xcsc->custom_ctrls[itr] = + v4l2_ctrl_new_custom(&xcsc->ctrl_handler, + &xcsc_color_ctrls[itr], NULL); + } + if (xcsc->ctrl_handler.error) { + dev_err(&pdev->dev, "Failed to add v4l2 controls"); + rval = xcsc->ctrl_handler.error; + goto ctrl_error; + } + subdev->ctrl_handler = &xcsc->ctrl_handler; + rval = v4l2_ctrl_handler_setup(&xcsc->ctrl_handler); + if (rval < 0) { + dev_err(xcsc->xvip.dev, "Failed to setup control handler"); + goto ctrl_error; + } + platform_set_drvdata(pdev, xcsc); + rval = v4l2_async_register_subdev(subdev); + if (rval < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto ctrl_error; + } + dev_info(&pdev->dev, "VPSS CSC %d-bit Color Depth Probe Successful", + xcsc->color_depth); + return 0; +ctrl_error: + v4l2_ctrl_handler_free(&xcsc->ctrl_handler); + media_entity_cleanup(&subdev->entity); +media_error: + xvip_cleanup_resources(&xcsc->xvip); + return rval; +} + +static int xcsc_remove(struct platform_device *pdev) +{ + struct xcsc_dev *xcsc = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xcsc->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&xcsc->ctrl_handler); + media_entity_cleanup(&subdev->entity); + xvip_cleanup_resources(&xcsc->xvip); + return 0; +} + +static const struct of_device_id xcsc_of_id_table[] = { + {.compatible = "xlnx,v-vpss-csc"}, + { } +}; + +MODULE_DEVICE_TABLE(of, xcsc_of_id_table); + +static struct platform_driver xcsc_driver = { + .driver = { + .name = "xilinx-vpss-csc", + .of_match_table = xcsc_of_id_table, + }, + .probe = xcsc_probe, + .remove = xcsc_remove, +}; + +module_platform_driver(xcsc_driver); +MODULE_DESCRIPTION("Xilinx VPSS CSC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-vpss-scaler.c b/drivers/media/platform/xilinx/xilinx-vpss-scaler.c new file mode 100644 index 000000000000..e07d7eaad259 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-vpss-scaler.c @@ -0,0 +1,1878 @@ +/* + * Xilinx VPSS Scaler + * + * Copyright (C) 2017 Xilinx, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-subdev.h> +#include "xilinx-vip.h" + +#define XSCALER_MIN_WIDTH (64) +#define XSCALER_MAX_WIDTH (8192) +#define XSCALER_MIN_HEIGHT (64) +#define XSCALER_MAX_HEIGHT (4320) +#define XSCALER_MAX_PHASES (64) + +/* Modify to defaults incase it is not configured from application */ +#define XSCALER_DEF_IN_HEIGHT (720) +#define XSCALER_DEF_IN_WIDTH (1280) +#define XSCALER_DEF_OUT_HEIGHT (1080) +#define XSCALER_DEF_OUT_WIDTH (1920) + +#define XSCALER_HSF (0x0100) +#define XSCALER_VSF (0x0104) +#define XSCALER_SF_SHIFT (20) +#define XSCALER_SF_MASK (0xffffff) +#define XSCALER_SOURCE_SIZE (0x0108) +#define XSCALER_SIZE_HORZ_SHIFT (0) +#define XSCALER_SIZE_VERT_SHIFT (16) +#define XSCALER_SIZE_MASK (0xfff) +#define XSCALER_HAPERTURE (0x010c) +#define XSCALER_VAPERTURE (0x0110) +#define XSCALER_APERTURE_START_SHIFT (0) +#define XSCALER_APERTURE_END_SHIFT (16) +#define XSCALER_OUTPUT_SIZE (0x0114) +#define XSCALER_COEF_DATA_IN (0x0134) +#define XSCALER_BITSHIFT_16 (16) + +/* Video subsytems block offset */ +#define S_AXIS_RESET_OFF (0x00010000) +#define V_HSCALER_OFF (0x00000000) +#define V_VSCALER_OFF (0x00020000) + +/* HW Reset Network GPIO Channel */ +#define XGPIO_CH_RESET_SEL (1) +#define XGPIO_RESET_MASK_VIDEO_IN BIT(0) +#define XGPIO_RESET_MASK_IP_AXIS BIT(1) +#define XGPIO_RESET_MASK_IP_AXIMM BIT(0) +#define XGPIO_RESET_MASK_ALL_BLOCKS (XGPIO_RESET_MASK_VIDEO_IN | \ + XGPIO_RESET_MASK_IP_AXIS) +#define XGPIO_DATA_OFFSET (0x0) +#define XGPIO_TRI_OFFSET (0x4) +#define XGPIO_DATA2_OFFSET (0x8) +#define XGPIO_TRI2_OFFSET (0xc) + +#define XGPIO_GIE_OFFSET (0x11c) +#define XGPIO_ISR_OFFSET (0x120) +#define XGPIO_IER_OFFSET (0x128) +#define XGPIO_CHAN_OFFSET (8) +#define STEP_PRECISION (65536) + +/* Video IP Formats */ +enum xscaler_vid_reg_fmts { + XVIDC_CSF_RGB = 0, + XVIDC_CSF_YCRCB_444, + XVIDC_CSF_YCRCB_422, + XVIDC_CSF_YCRCB_420, +}; + +/* Video IP PPC */ +#define XSCALER_PPC_1 (1) +#define XSCALER_PPC_2 (2) + +#define XV_HSCALER_MAX_H_TAPS (12) +#define XV_HSCALER_MAX_H_PHASES (64) +#define XV_HSCALER_MAX_LINE_WIDTH (3840) +#define XV_VSCALER_MAX_V_TAPS (12) +#define XV_VSCALER_MAX_V_PHASES (64) + +#define XV_HSCALER_TAPS_2 (2) +#define XV_HSCALER_TAPS_4 (4) +#define XV_HSCALER_TAPS_6 (6) +#define XV_HSCALER_TAPS_8 (8) +#define XV_HSCALER_TAPS_10 (10) +#define XV_HSCALER_TAPS_12 (12) +#define XV_VSCALER_TAPS_2 (2) +#define XV_VSCALER_TAPS_4 (4) +#define XV_VSCALER_TAPS_6 (6) +#define XV_VSCALER_TAPS_8 (8) +#define XV_VSCALER_TAPS_10 (10) +#define XV_VSCALER_TAPS_12 (12) + +/* 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) +#define XHSC_STEP_PRECISION_SHIFT (16) +#define XHSC_HPHASE_SHIFT_BY_6 (6) +#define XHSC_HPHASE_MULTIPLIER (9) + +/* Mask definitions for Low and high 16 bits in a 32 bit number */ +#define XVSC_MASK_LOW_16BITS GENMASK(15, 0) +#define XVSC_MASK_HIGH_16BITS GENMASK(31, 16) + +/* XSCALER POWER MACROS */ +#define XSCALER_RESET_ASSERT (0x1) +#define XSCALER_RESET_DEASSERT (0x0) + +/* Scaler AP Control Registers */ +#define XSCALER_START BIT(0) +#define XSCALER_AUTO_RESTART BIT(7) +#define XSCALER_STREAM_ON (XSCALER_START | XSCALER_AUTO_RESTART) + +/* H-scaler registers */ +#define XV_HSCALER_CTRL_ADDR_AP_CTRL (0x0000) +#define XV_HSCALER_CTRL_ADDR_GIE (0x0004) +#define XV_HSCALER_CTRL_ADDR_IER (0x0008) +#define XV_HSCALER_CTRL_ADDR_ISR (0x000c) +#define XV_HSCALER_CTRL_ADDR_HWREG_HEIGHT_DATA (0x0010) +#define XV_HSCALER_CTRL_ADDR_HWREG_WIDTHIN_DATA (0x0018) +#define XV_HSCALER_CTRL_ADDR_HWREG_WIDTHOUT_DATA (0x0020) +#define XV_HSCALER_CTRL_ADDR_HWREG_COLORMODE_DATA (0x0028) +#define XV_HSCALER_CTRL_ADDR_HWREG_PIXELRATE_DATA (0x0030) +#define XV_HSCALER_CTRL_ADDR_HWREG_COLORMODEOUT_DATA (0X0038) +#define XV_HSCALER_CTRL_ADDR_HWREG_HFLTCOEFF_BASE (0x0800) +#define XV_HSCALER_CTRL_ADDR_HWREG_HFLTCOEFF_HIGH (0x0bff) + +/* H-scaler coefficients for 6, 8, 10 and 12 tap filters */ +static const u16 +xhsc_coeff_taps6[XV_HSCALER_MAX_H_PHASES][XV_HSCALER_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[XV_HSCALER_MAX_H_PHASES][XV_HSCALER_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[XV_HSCALER_MAX_H_PHASES][XV_HSCALER_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[XV_HSCALER_MAX_H_PHASES][XV_HSCALER_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, }, +}; + +#define XV_HSCALER_CTRL_WIDTH_HWREG_HFLTCOEFF (16) +#define XV_HSCALER_CTRL_DEPTH_HWREG_HFLTCOEFF (384) +#define XV_HSCALER_CTRL_ADDR_HWREG_PHASESH_V_BASE (0x2000) +#define XV_HSCALER_CTRL_ADDR_HWREG_PHASESH_V_HIGH (0x3fff) +#define XV_HSCALER_CTRL_WIDTH_HWREG_PHASESH_V (18) +#define XV_HSCALER_CTRL_DEPTH_HWREG_PHASESH_V (1920) + +/* H-scaler masks */ +#define XV_HSCALER_PHASESH_V_OUTPUT_WR_EN BIT(8) + +/* V-scaler registers */ +#define XV_VSCALER_CTRL_ADDR_AP_CTRL (0x000) +#define XV_VSCALER_CTRL_ADDR_GIE (0x004) +#define XV_VSCALER_CTRL_ADDR_IER (0x008) +#define XV_VSCALER_CTRL_ADDR_ISR (0x00c) +#define XV_VSCALER_CTRL_ADDR_HWREG_HEIGHTIN_DATA (0x010) +#define XV_VSCALER_CTRL_ADDR_HWREG_WIDTH_DATA (0x018) +#define XV_VSCALER_CTRL_ADDR_HWREG_HEIGHTOUT_DATA (0x020) +#define XV_VSCALER_CTRL_ADDR_HWREG_LINERATE_DATA (0x028) +#define XV_VSCALER_CTRL_ADDR_HWREG_COLORMODE_DATA (0x030) +#define XV_VSCALER_CTRL_ADDR_HWREG_VFLTCOEFF_BASE (0x800) +#define XV_VSCALER_CTRL_ADDR_HWREG_VFLTCOEFF_HIGH (0xbff) + +/* V-scaler coefficients for 6, 8, 10 and 12 tap filters */ +static const u16 +xvsc_coeff_taps6[XV_VSCALER_MAX_V_PHASES][XV_VSCALER_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[XV_VSCALER_MAX_V_PHASES][XV_VSCALER_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[XV_VSCALER_MAX_V_PHASES][XV_VSCALER_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[XV_VSCALER_MAX_V_PHASES][XV_VSCALER_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, }, +}; + +#define XV_VSCALER_CTRL_WIDTH_HWREG_VFLTCOEFF (16) +#define XV_VSCALER_CTRL_DEPTH_HWREG_VFLTCOEFF (384) + +#define XSCALER_CLK_PROP BIT(0) + +/** + * struct xscaler_feature - dt or IP property structure + * @flags: Bitmask of properties enabled in IP or dt + */ +struct xscaler_feature { + u32 flags; +}; + +/** + * struct xscaler_device - Xilinx Scaler device structure + * @xvip: Xilinx Video IP device + * @pads: Scaler sub-device media pads + * @formats: V4L2 media bus formats at the sink and source pads + * @default_formats: default V4L2 media bus formats + * @vip_formats: Xilinx Video IP format retrieved from the DT + * @num_hori_taps: number of horizontal taps + * @num_vert_taps: number of vertical taps + * @max_num_phases: maximum number of phases + * @pix_per_clk: Pixels per Clock cycle the IP operates upon + * @max_pixels: The maximum number of pixels that the H-scaler examines + * @max_lines: The maximum number of lines that the V-scaler examines + * @H_phases: The phases needed to program the H-scaler for different taps + * @hscaler_coeff: The complete array of H-scaler coefficients + * @vscaler_coeff: The complete array of V-scaler coefficients + * @is_polyphase: Track if scaling algorithm is polyphase or not + * @rst_gpio: GPIO reset line to bring VPSS Scaler out of reset + * @cfg: Pointer to scaler config structure + * @aclk_axis: AXI4-Stream video interface clock + * @aclk_ctrl: AXI4-Lite control interface clock + */ +struct xscaler_device { + struct xvip_device xvip; + + struct media_pad pads[2]; + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct xvip_video_format *vip_formats[2]; + + u32 num_hori_taps; + u32 num_vert_taps; + u32 max_num_phases; + u32 pix_per_clk; + u32 max_pixels; + u32 max_lines; + u32 H_phases[XV_HSCALER_MAX_LINE_WIDTH]; + short hscaler_coeff[XV_HSCALER_MAX_H_PHASES][XV_HSCALER_MAX_H_TAPS]; + short vscaler_coeff[XV_VSCALER_MAX_V_PHASES][XV_VSCALER_MAX_V_TAPS]; + bool is_polyphase; + + struct gpio_desc *rst_gpio; + const struct xscaler_feature *cfg; + struct clk *aclk_axis; + struct clk *aclk_ctrl; +}; + +static const struct xscaler_feature xlnx_scaler_v1_0 = { + .flags = XSCALER_CLK_PROP, +}; + +static const struct xscaler_feature xlnx_scaler = { + .flags = 0, +}; + +static const struct of_device_id xscaler_of_id_table[] = { + { .compatible = "xlnx,v-vpss-scaler", + .data = &xlnx_scaler}, + { .compatible = "xlnx,v-vpss-scaler-1.0", + .data = &xlnx_scaler_v1_0}, + { /* end of table */ } +}; +MODULE_DEVICE_TABLE(of, xscaler_of_id_table); + +static inline struct xscaler_device *to_scaler(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct xscaler_device, xvip.subdev); +} + +static void +xv_hscaler_calculate_phases(struct xscaler_device *xscaler, + u32 width_in, u32 width_out, u32 pixel_rate) +{ + unsigned int loop_width; + unsigned int x, s; + int offset = 0; + int xwrite_pos = 0; + bool output_write_en; + bool get_new_pix; + u64 phaseH; + u32 array_idx = 0; + int nr_rds; + int nr_rds_clck; + unsigned int nphases = xscaler->max_num_phases; + unsigned int nppc = xscaler->pix_per_clk; + unsigned int shift = XHSC_STEP_PRECISION_SHIFT - ilog2(nphases); + + loop_width = max_t(u32, width_in, width_out); + loop_width = ALIGN(loop_width + nppc - 1, nppc); + + for (x = 0; x < loop_width; x++) { + nr_rds_clck = 0; + for (s = 0; s < nppc; s++) { + phaseH = (offset >> shift) & (nphases - 1); + get_new_pix = false; + output_write_en = false; + if ((offset >> XHSC_STEP_PRECISION_SHIFT) != 0) { + /* read a new input sample */ + get_new_pix = true; + offset -= (1 << XHSC_STEP_PRECISION_SHIFT); + array_idx++; + } + + if (((offset >> XHSC_STEP_PRECISION_SHIFT) == 0) && + (xwrite_pos < width_out)) { + /* produce a new output sample */ + offset += pixel_rate; + output_write_en = true; + xwrite_pos++; + } + + /* Needs updates for 4 PPC */ + xscaler->H_phases[x] |= (phaseH << + (s * XHSC_HPHASE_MULTIPLIER)); + xscaler->H_phases[x] |= (array_idx << + (XHSC_HPHASE_SHIFT_BY_6 + + (s * XHSC_HPHASE_MULTIPLIER))); + if (output_write_en) { + xscaler->H_phases[x] |= + (XV_HSCALER_PHASESH_V_OUTPUT_WR_EN << + (s * XHSC_HPHASE_MULTIPLIER)); + } + + if (get_new_pix) + nr_rds_clck++; + } + if (array_idx >= nppc) + array_idx &= (nppc - 1); + + nr_rds += nr_rds_clck; + if (nr_rds >= nppc) + nr_rds -= nppc; + } +} + +static void +xv_hscaler_load_ext_coeff(struct xscaler_device *xscaler, + const short *coeff, u32 ntaps) +{ + unsigned int i, j, pad, offset; + u32 nphases = xscaler->max_num_phases; + + /* Determine if coefficient needs padding (effective vs. max taps) */ + pad = XV_HSCALER_MAX_H_TAPS - ntaps; + offset = pad >> 1; + dev_dbg(xscaler->xvip.dev, + "%s : Pad = %d Offset = %d Nphases = %d ntaps = %d", + __func__, pad, offset, nphases, ntaps); + + /* 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]; + } + + if (pad) { /* effective taps < max_taps */ + for (i = 0; i < nphases; i++) { + /* pad left */ + for (j = 0; j < offset; j++) + xscaler->hscaler_coeff[i][j] = 0; + /* pad right */ + j = ntaps + offset; + for (; j < XV_HSCALER_MAX_H_TAPS; j++) + xscaler->hscaler_coeff[i][j] = 0; + } + } +} + +/** + * xv_hscaler_coeff_select - Selection of H-Scaler coefficients of operation + * @xscaler: VPSS Scaler device information + * @width_in: Width of input video + * @width_out: Width of desired output video + * + * 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 int +xv_hscaler_select_coeff(struct xscaler_device *xscaler, + u32 width_in, u32 width_out) +{ + const short *coeff; + u16 hscale_ratio; + u32 ntaps = xscaler->num_hori_taps; + + /* + * Scale Down Mode will use dynamic filter selection logic + * Scale Up Mode (including 1:1) will always use 6 tap filter + */ + if (width_out < width_in) { + hscale_ratio = ((width_in * 10) / width_out); + + switch (xscaler->num_hori_taps) { + case XV_HSCALER_TAPS_6: + coeff = &xhsc_coeff_taps6[0][0]; + ntaps = XV_HSCALER_TAPS_6; + break; + case XV_HSCALER_TAPS_8: + if (hscale_ratio > 15) { + coeff = &xhsc_coeff_taps8[0][0]; + ntaps = XV_HSCALER_TAPS_8; + } else { + coeff = &xhsc_coeff_taps6[0][0]; + ntaps = XV_HSCALER_TAPS_6; + } + break; + case XV_HSCALER_TAPS_10: + if (hscale_ratio > 25) { + coeff = &xhsc_coeff_taps10[0][0]; + ntaps = XV_HSCALER_TAPS_10; + } else if (hscale_ratio > 15) { + coeff = &xhsc_coeff_taps8[0][0]; + ntaps = XV_HSCALER_TAPS_8; + } else { + coeff = &xhsc_coeff_taps6[0][0]; + ntaps = XV_HSCALER_TAPS_6; + } + break; + case XV_HSCALER_TAPS_12: + if (hscale_ratio > 35) { + coeff = &xhsc_coeff_taps12[0][0]; + ntaps = XV_HSCALER_TAPS_12; + } else if (hscale_ratio > 25) { + coeff = &xhsc_coeff_taps10[0][0]; + ntaps = XV_HSCALER_TAPS_10; + } else if (hscale_ratio > 15) { + coeff = &xhsc_coeff_taps8[0][0]; + ntaps = XV_HSCALER_TAPS_8; + } else { + coeff = &xhsc_coeff_taps6[0][0]; + ntaps = XV_HSCALER_TAPS_6; + } + break; + default: + dev_err(xscaler->xvip.dev, + "Unsupported H-scaler number of taps = %d", + xscaler->num_hori_taps); + return -EINVAL; + } + } else { + dev_dbg(xscaler->xvip.dev, "H-scaler : scale up 6 tap"); + coeff = &xhsc_coeff_taps6[0][0]; + ntaps = XV_HSCALER_TAPS_6; + } + xv_hscaler_load_ext_coeff(xscaler, coeff, ntaps); + return 0; +} + +static void xv_hscaler_set_coeff(struct xscaler_device *xscaler) +{ + int val, i, j, offset, rd_indx; + u32 ntaps = xscaler->num_hori_taps; + u32 nphases = xscaler->max_num_phases; + u32 base_addr; + + offset = (XV_HSCALER_MAX_H_TAPS - ntaps) / 2; + base_addr = V_HSCALER_OFF + XV_HSCALER_CTRL_ADDR_HWREG_HFLTCOEFF_BASE; + 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->xvip, base_addr + + ((i * ntaps / 2 + j) * 4), val); + } + } +} + +static void +xv_vscaler_load_ext_coeff(struct xscaler_device *xscaler, + const short *coeff, u32 ntaps) +{ + int i, j, pad, offset; + u32 nphases = xscaler->max_num_phases; + + /* Determine if coefficient needs padding (effective vs. max taps) */ + pad = XV_VSCALER_MAX_V_TAPS - ntaps; + offset = pad ? (pad >> 1) : 0; + + dev_dbg(xscaler->xvip.dev, + "%s : Pad = %d Offset = %d Nphases = %d ntaps = %d", + __func__, pad, offset, nphases, ntaps); + + /* 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]; + } + + if (pad) { /* effective taps < max_taps */ + for (i = 0; i < nphases; i++) { + /* pad left */ + for (j = 0; j < offset; j++) + xscaler->vscaler_coeff[i][j] = 0; + /* pad right */ + j = ntaps + offset; + for (; j < XV_VSCALER_MAX_V_TAPS; j++) + xscaler->vscaler_coeff[i][j] = 0; + } + } +} + +static void xv_vscaler_set_coeff(struct xscaler_device *xscaler) +{ + u32 nphases = xscaler->max_num_phases; + u32 ntaps = xscaler->num_vert_taps; + int val, i, j, offset, rd_indx; + u32 base_addr; + + offset = (XV_VSCALER_MAX_V_TAPS - ntaps) / 2; + base_addr = V_VSCALER_OFF + XV_VSCALER_CTRL_ADDR_HWREG_VFLTCOEFF_BASE; + + 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->xvip, + base_addr + ((i * ntaps / 2 + j) * 4), val); + } + } +} + +/** + * xv_vscaler_coeff_select - Selection of V-Scaler coefficients of operation + * @xscaler: VPSS Scaler device information + * @height_in: Height of input video + * @height_out: Height of desired output video + * + * 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 10-tap + * filter may operate with 6 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 + * V-scaler number of taps. + */ +static int +xv_vscaler_select_coeff(struct xscaler_device *xscaler, + u32 height_in, u32 height_out) +{ + const short *coeff; + u16 vscale_ratio; + u32 ntaps = xscaler->num_vert_taps; + + /* + * Scale Down Mode will use dynamic filter selection logic + * Scale Up Mode (including 1:1) will always use 6 tap filter + */ + + if (height_out < height_in) { + vscale_ratio = ((height_in * 10) / height_out); + + switch (xscaler->num_vert_taps) { + case XV_VSCALER_TAPS_6: + coeff = &xvsc_coeff_taps6[0][0]; + ntaps = XV_VSCALER_TAPS_6; + break; + case XV_VSCALER_TAPS_8: + if (vscale_ratio > 15) { + coeff = &xvsc_coeff_taps8[0][0]; + ntaps = XV_VSCALER_TAPS_8; + } else { + coeff = &xvsc_coeff_taps6[0][0]; + ntaps = XV_VSCALER_TAPS_6; + } + break; + case XV_VSCALER_TAPS_10: + if (vscale_ratio > 25) { + coeff = &xvsc_coeff_taps10[0][0]; + ntaps = XV_VSCALER_TAPS_10; + } else if (vscale_ratio > 15) { + coeff = &xvsc_coeff_taps8[0][0]; + ntaps = XV_VSCALER_TAPS_8; + } else { + coeff = &xvsc_coeff_taps6[0][0]; + ntaps = XV_VSCALER_TAPS_6; + } + break; + case XV_VSCALER_TAPS_12: + if (vscale_ratio > 35) { + coeff = &xvsc_coeff_taps12[0][0]; + ntaps = XV_VSCALER_TAPS_12; + } else if (vscale_ratio > 25) { + coeff = &xvsc_coeff_taps10[0][0]; + ntaps = XV_VSCALER_TAPS_10; + } else if (vscale_ratio > 15) { + coeff = &xvsc_coeff_taps8[0][0]; + ntaps = XV_VSCALER_TAPS_8; + } else { + coeff = &xvsc_coeff_taps6[0][0]; + ntaps = XV_VSCALER_TAPS_6; + } + break; + default: + dev_err(xscaler->xvip.dev, + "Unsupported V-scaler number of taps = %d", + xscaler->num_vert_taps); + return -EINVAL; + } + } else { + dev_dbg(xscaler->xvip.dev, "V-scaler : scale up 6 tap"); + coeff = &xvsc_coeff_taps6[0][0]; + ntaps = XV_VSCALER_TAPS_6; + } + + xv_vscaler_load_ext_coeff(xscaler, coeff, ntaps); + return 0; +} + +/* + * V4L2 Subdevice Video Operations + */ + +static inline void +xv_procss_disable_block(struct xvip_device *xvip, u32 channel, u32 ip_block) +{ + xvip_clr(xvip, ((channel - 1) * XGPIO_CHAN_OFFSET) + + XGPIO_DATA_OFFSET + S_AXIS_RESET_OFF, + ip_block); +} + +static inline void +xv_procss_enable_block(struct xvip_device *xvip, u32 channel, u32 ip_block) +{ + xvip_set(xvip, ((channel - 1) * XGPIO_CHAN_OFFSET) + + XGPIO_DATA_OFFSET + S_AXIS_RESET_OFF, + ip_block); +} + +static void xscaler_reset(struct xscaler_device *xscaler) +{ + xv_procss_disable_block(&xscaler->xvip, XGPIO_CH_RESET_SEL, + XGPIO_RESET_MASK_ALL_BLOCKS); + xv_procss_enable_block(&xscaler->xvip, XGPIO_CH_RESET_SEL, + XGPIO_RESET_MASK_IP_AXIS); +} + +static int +xv_vscaler_setup_video_fmt(struct xscaler_device *xscaler, u32 code_in) +{ + u32 video_in; + + switch (code_in) { + case MEDIA_BUS_FMT_VYYUYY8_1X24: + case MEDIA_BUS_FMT_VYYUYY10_4X20: + dev_dbg(xscaler->xvip.dev, + "Vscaler Input Media Format YUV 420"); + video_in = XVIDC_CSF_YCRCB_420; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + dev_dbg(xscaler->xvip.dev, + "Vscaler Input Media Format YUV 422"); + video_in = XVIDC_CSF_YCRCB_422; + break; + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_VUY10_1X30: + dev_dbg(xscaler->xvip.dev, + "Vscaler Input Media Format YUV 444"); + video_in = XVIDC_CSF_YCRCB_444; + break; + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RBG101010_1X30: + dev_dbg(xscaler->xvip.dev, + "Vscaler Input Media Format RGB"); + video_in = XVIDC_CSF_RGB; + break; + default: + dev_err(xscaler->xvip.dev, + "Vscaler Unsupported Input Media Format 0x%x", + code_in); + return -EINVAL; + } + xvip_write(&xscaler->xvip, V_VSCALER_OFF + + XV_VSCALER_CTRL_ADDR_HWREG_COLORMODE_DATA, + video_in); + /* + * Vscaler will upscale to YUV 422 before + * Hscaler starts operation + */ + if (video_in == XVIDC_CSF_YCRCB_420) + return XVIDC_CSF_YCRCB_422; + return video_in; +} + +static int xv_hscaler_setup_video_fmt(struct xscaler_device *xscaler, + u32 code_out, u32 vsc_out) +{ + u32 video_out; + + switch (vsc_out) { + case XVIDC_CSF_YCRCB_422: + dev_dbg(xscaler->xvip.dev, + "Hscaler Input Media Format is YUV 422"); + break; + case XVIDC_CSF_YCRCB_444: + dev_dbg(xscaler->xvip.dev, + "Hscaler Input Media Format is YUV 444"); + break; + case XVIDC_CSF_RGB: + dev_dbg(xscaler->xvip.dev, + "Hscaler Input Media Format is RGB"); + break; + default: + dev_err(xscaler->xvip.dev, + "Hscaler got unsupported format from Vscaler"); + return -EINVAL; + } + + xvip_write(&xscaler->xvip, V_HSCALER_OFF + + XV_HSCALER_CTRL_ADDR_HWREG_COLORMODE_DATA, + vsc_out); + + switch (code_out) { + case MEDIA_BUS_FMT_VYYUYY8_1X24: + case MEDIA_BUS_FMT_VYYUYY10_4X20: + dev_dbg(xscaler->xvip.dev, + "Hscaler Output Media Format YUV 420\n"); + video_out = XVIDC_CSF_YCRCB_420; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + dev_dbg(xscaler->xvip.dev, + "Hscaler Output Media Format YUV 422\n"); + video_out = XVIDC_CSF_YCRCB_422; + break; + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_VUY10_1X30: + dev_dbg(xscaler->xvip.dev, + "Hscaler Output Media Format YUV 444\n"); + video_out = XVIDC_CSF_YCRCB_444; + break; + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RBG101010_1X30: + dev_dbg(xscaler->xvip.dev, + "Hscaler Output Media Format RGB\n"); + video_out = XVIDC_CSF_RGB; + break; + default: + dev_err(xscaler->xvip.dev, + "Hscaler Unsupported Output Media Format 0x%x", + code_out); + return -EINVAL; + } + xvip_write(&xscaler->xvip, V_HSCALER_OFF + + XV_HSCALER_CTRL_ADDR_HWREG_COLORMODEOUT_DATA, + video_out); + return 0; +} + +static void +xv_hscaler_set_phases(struct xscaler_device *xscaler) +{ + u32 loop_width; + u32 index, val; + u32 offset, i, lsb, msb; + + loop_width = xscaler->max_pixels / xscaler->pix_per_clk; + offset = V_HSCALER_OFF + XV_HSCALER_CTRL_ADDR_HWREG_PHASESH_V_BASE; + + switch (xscaler->pix_per_clk) { + case XSCALER_PPC_1: + /* + * phaseH is 64 bits but only lower 16 bits of each entry + * is valid .Form a 32 bit word with 16bit LSB from 2 + * consecutive entries. Need 1 32b write to get 2 entries + * into IP registers (i is array loc and index is + * address offset) + */ + index = 0; + for (i = 0; i < loop_width; i += 2) { + lsb = xscaler->H_phases[i] & XHSC_MASK_LOW_16BITS; + msb = xscaler->H_phases[i + 1] & XHSC_MASK_LOW_16BITS; + val = (msb << 16 | lsb); + xvip_write(&xscaler->xvip, offset + (index * 4), val); + ++index; + } + dev_dbg(xscaler->xvip.dev, + "%s : Operating in 1 PPC design", __func__); + return; + case XSCALER_PPC_2: + /* + * PhaseH is 64bits but only lower 32b of each entry is valid + * Need 1 32b write to get each entry into IP registers + */ + for (i = 0; i < loop_width; i++) { + val = (xscaler->H_phases[i] & + XHSC_MASK_LOW_32BITS); + xvip_write(&xscaler->xvip, offset + (i * 4), val); + } + dev_dbg(xscaler->xvip.dev, + "%s : Operating in 2 PPC design", __func__); + return; + } +} + +static int xscaler_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct xscaler_device *xscaler = to_scaler(subdev); + u32 width_in, width_out; + u32 height_in, height_out; + u32 code_in, code_out; + u32 pixel_rate; + u32 line_rate; + int ret; + + if (!enable) { + dev_dbg(xscaler->xvip.dev, "%s: Stream Off", __func__); + /* Reset the Global IP Reset through PS GPIO */ + gpiod_set_value_cansleep(xscaler->rst_gpio, + XSCALER_RESET_ASSERT); + gpiod_set_value_cansleep(xscaler->rst_gpio, + XSCALER_RESET_DEASSERT); + xscaler_reset(xscaler); + memset(xscaler->H_phases, 0, sizeof(xscaler->H_phases)); + return 0; + } + + dev_dbg(xscaler->xvip.dev, "%s: Stream On", __func__); + + /* Extract Sink Pad Information */ + width_in = xscaler->formats[XVIP_PAD_SINK].width; + height_in = xscaler->formats[XVIP_PAD_SINK].height; + code_in = xscaler->formats[XVIP_PAD_SINK].code; + + /* Extract Source Pad Information */ + width_out = xscaler->formats[XVIP_PAD_SOURCE].width; + height_out = xscaler->formats[XVIP_PAD_SOURCE].height; + code_out = xscaler->formats[XVIP_PAD_SOURCE].code; + + /* + * V Scaler is before H Scaler + * V-Scaler_setup + */ + line_rate = (height_in * STEP_PRECISION) / height_out; + + if (xscaler->is_polyphase) { + ret = xv_vscaler_select_coeff(xscaler, height_in, height_out); + if (ret < 0) + return ret; + xv_vscaler_set_coeff(xscaler); + } + + xvip_write(&xscaler->xvip, V_VSCALER_OFF + + XV_VSCALER_CTRL_ADDR_HWREG_HEIGHTIN_DATA, height_in); + xvip_write(&xscaler->xvip, V_VSCALER_OFF + + XV_VSCALER_CTRL_ADDR_HWREG_WIDTH_DATA, width_in); + xvip_write(&xscaler->xvip, V_VSCALER_OFF + + XV_VSCALER_CTRL_ADDR_HWREG_HEIGHTOUT_DATA, height_out); + xvip_write(&xscaler->xvip, V_VSCALER_OFF + + XV_VSCALER_CTRL_ADDR_HWREG_LINERATE_DATA, line_rate); + ret = xv_vscaler_setup_video_fmt(xscaler, code_in); + if (ret < 0) + return ret; + + /* H-Scaler_setup */ + pixel_rate = (width_in * STEP_PRECISION) / width_out; + + xvip_write(&xscaler->xvip, V_HSCALER_OFF + + XV_HSCALER_CTRL_ADDR_HWREG_HEIGHT_DATA, height_out); + xvip_write(&xscaler->xvip, V_HSCALER_OFF + + XV_HSCALER_CTRL_ADDR_HWREG_WIDTHIN_DATA, width_in); + xvip_write(&xscaler->xvip, V_HSCALER_OFF + + XV_HSCALER_CTRL_ADDR_HWREG_WIDTHOUT_DATA, width_out); + xvip_write(&xscaler->xvip, V_HSCALER_OFF + + XV_HSCALER_CTRL_ADDR_HWREG_PIXELRATE_DATA, pixel_rate); + ret = xv_hscaler_setup_video_fmt(xscaler, code_out, ret); + if (ret < 0) + return ret; + + if (xscaler->is_polyphase) { + ret = xv_hscaler_select_coeff(xscaler, width_in, width_out); + if (ret < 0) + return ret; + xv_hscaler_set_coeff(xscaler); + } + + xv_hscaler_calculate_phases(xscaler, width_in, width_out, pixel_rate); + xv_hscaler_set_phases(xscaler); + + /* Start Scaler sub-cores */ + xvip_write(&xscaler->xvip, V_HSCALER_OFF + + XV_HSCALER_CTRL_ADDR_AP_CTRL, XSCALER_STREAM_ON); + xvip_write(&xscaler->xvip, V_VSCALER_OFF + + XV_VSCALER_CTRL_ADDR_AP_CTRL, XSCALER_STREAM_ON); + xv_procss_enable_block(&xscaler->xvip, XGPIO_CH_RESET_SEL, + XGPIO_RESET_MASK_VIDEO_IN); + return 0; +} + +/* + * V4L2 Subdevice Pad Operations + */ + +static int xscaler_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + struct xscaler_device *xscaler = to_scaler(subdev); + + format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad); + if (fse->index || fse->code != format->code) + return -EINVAL; + + fse->min_width = XSCALER_MIN_WIDTH; + fse->max_width = xscaler->max_pixels; + fse->min_height = XSCALER_MIN_HEIGHT; + fse->max_height = xscaler->max_lines; + + return 0; +} + +static struct v4l2_mbus_framefmt * +__xscaler_get_pad_format(struct xscaler_device *xscaler, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&xscaler->xvip.subdev, cfg, + pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &xscaler->formats[pad]; + default: + return NULL; + } +} + +static int xscaler_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xscaler_device *xscaler = to_scaler(subdev); + + fmt->format = *__xscaler_get_pad_format(xscaler, cfg, fmt->pad, + fmt->which); + + return 0; +} + +static int xscaler_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct xscaler_device *xscaler = to_scaler(subdev); + struct v4l2_mbus_framefmt *format; + + format = __xscaler_get_pad_format(xscaler, cfg, fmt->pad, fmt->which); + *format = fmt->format; + + format->width = clamp_t(unsigned int, fmt->format.width, + XSCALER_MIN_WIDTH, xscaler->max_pixels); + format->height = clamp_t(unsigned int, fmt->format.height, + XSCALER_MIN_HEIGHT, xscaler->max_lines); + format->code = fmt->format.code; + fmt->format = *format; + return 0; +} + +/* + * V4L2 Subdevice Operations + */ + +static int +xscaler_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct xscaler_device *xscaler = to_scaler(subdev); + struct v4l2_mbus_framefmt *format; + + /* Initialize with default formats */ + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK); + *format = xscaler->default_formats[XVIP_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE); + *format = xscaler->default_formats[XVIP_PAD_SOURCE]; + + return 0; +} + +static int +xscaler_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static struct v4l2_subdev_video_ops xscaler_video_ops = { + .s_stream = xscaler_s_stream, +}; + +static struct v4l2_subdev_pad_ops xscaler_pad_ops = { + .enum_mbus_code = xvip_enum_mbus_code, + .enum_frame_size = xscaler_enum_frame_size, + .get_fmt = xscaler_get_format, + .set_fmt = xscaler_set_format, +}; + +static struct v4l2_subdev_ops xscaler_ops = { + .video = &xscaler_video_ops, + .pad = &xscaler_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops xscaler_internal_ops = { + .open = xscaler_open, + .close = xscaler_close, +}; + +/* + * Media Operations + */ + +static const struct media_entity_operations xscaler_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * Platform Device Driver + */ + +static int xscaler_parse_of(struct xscaler_device *xscaler) +{ + struct device *dev = xscaler->xvip.dev; + struct device_node *node = xscaler->xvip.dev->of_node; + const struct xvip_video_format *vip_format; + struct device_node *ports; + struct device_node *port; + int ret; + u32 port_id, dt_ppc; + + if (xscaler->cfg->flags & XSCALER_CLK_PROP) { + xscaler->aclk_axis = devm_clk_get(dev, "aclk_axis"); + if (IS_ERR(xscaler->aclk_axis)) { + ret = PTR_ERR(xscaler->aclk_axis); + dev_err(dev, "failed to get aclk_axis (%d)\n", ret); + return ret; + } + xscaler->aclk_ctrl = devm_clk_get(dev, "aclk_ctrl"); + if (IS_ERR(xscaler->aclk_ctrl)) { + ret = PTR_ERR(xscaler->aclk_ctrl); + dev_err(dev, "failed to get aclk_ctrl (%d)\n", ret); + return ret; + } + } else { + dev_info(dev, "assuming all required clocks are enabled!\n"); + } + + ret = of_property_read_u32(node, "xlnx,max-height", + &xscaler->max_lines); + if (ret < 0) { + dev_err(dev, "xlnx,max-height is missing!"); + return -EINVAL; + } else if (xscaler->max_lines > XSCALER_MAX_HEIGHT || + xscaler->max_lines < XSCALER_MIN_HEIGHT) { + dev_err(dev, "Invalid height in dt"); + return -EINVAL; + } + + ret = of_property_read_u32(node, "xlnx,max-width", + &xscaler->max_pixels); + if (ret < 0) { + dev_err(dev, "xlnx,max-width is missing!"); + return -EINVAL; + } else if (xscaler->max_pixels > XSCALER_MAX_WIDTH || + xscaler->max_pixels < XSCALER_MIN_WIDTH) { + dev_err(dev, "Invalid width in dt"); + return -EINVAL; + } + + ports = of_get_child_by_name(node, "ports"); + if (!ports) + ports = node; + + /* Get the format description for each pad */ + for_each_child_of_node(ports, port) { + if (port->name && (of_node_cmp(port->name, "port") == 0)) { + vip_format = xvip_of_get_format(port); + if (IS_ERR(vip_format)) { + dev_err(dev, "invalid format in DT"); + return PTR_ERR(vip_format); + } + + ret = of_property_read_u32(port, "reg", &port_id); + if (ret < 0) { + dev_err(dev, "No reg in DT"); + return ret; + } + + if (port_id != 0 && port_id != 1) { + dev_err(dev, "Invalid reg in DT"); + return -EINVAL; + } + xscaler->vip_formats[port_id] = vip_format; + } + } + + ret = of_property_read_u32(node, "xlnx,num-hori-taps", + &xscaler->num_hori_taps); + if (ret < 0) + return ret; + + switch (xscaler->num_hori_taps) { + case XV_HSCALER_TAPS_2: + case XV_HSCALER_TAPS_4: + xscaler->is_polyphase = false; + break; + case XV_HSCALER_TAPS_6: + case XV_HSCALER_TAPS_8: + case XV_HSCALER_TAPS_10: + case XV_HSCALER_TAPS_12: + xscaler->is_polyphase = true; + break; + default: + dev_err(dev, "Unsupported num-hori-taps %d", + xscaler->num_hori_taps); + return -EINVAL; + } + + ret = of_property_read_u32(node, "xlnx,num-vert-taps", + &xscaler->num_vert_taps); + if (ret < 0) + return ret; + + /* + * For Bilinear and Bicubic case + * number of vertical and horizontal taps must match + */ + switch (xscaler->num_vert_taps) { + case XV_HSCALER_TAPS_2: + case XV_VSCALER_TAPS_4: + if (xscaler->num_vert_taps != xscaler->num_hori_taps) { + dev_err(dev, + "H-scaler taps %d mismatches V-scaler taps %d", + xscaler->num_hori_taps, + xscaler->num_vert_taps); + return -EINVAL; + } + break; + case XV_VSCALER_TAPS_6: + case XV_VSCALER_TAPS_8: + case XV_VSCALER_TAPS_10: + case XV_VSCALER_TAPS_12: + xscaler->is_polyphase = true; + break; + default: + dev_err(dev, "Unsupported num-vert-taps %d", + xscaler->num_vert_taps); + return -EINVAL; + } + + ret = of_property_read_u32(node, "xlnx,pix-per-clk", &dt_ppc); + if (ret < 0) + return ret; + + /* Driver only supports 1 PPC and 2 PPC */ + if (dt_ppc != XSCALER_PPC_1 && dt_ppc != XSCALER_PPC_2) { + dev_err(xscaler->xvip.dev, + "Unsupported xlnx,pix-per-clk(%d) value in DT", dt_ppc); + return -EINVAL; + } + xscaler->pix_per_clk = dt_ppc; + + /* Reset GPIO */ + xscaler->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(xscaler->rst_gpio)) { + if (PTR_ERR(xscaler->rst_gpio) != -EPROBE_DEFER) + dev_err(dev, "Reset GPIO not setup in DT"); + return PTR_ERR(xscaler->rst_gpio); + } + + return 0; +} + +static int xscaler_probe(struct platform_device *pdev) +{ + struct xscaler_device *xscaler; + struct v4l2_subdev *subdev; + struct v4l2_mbus_framefmt *default_format; + int ret; + const struct of_device_id *match; + struct device_node *node = pdev->dev.of_node; + struct resource *res; + + xscaler = devm_kzalloc(&pdev->dev, sizeof(*xscaler), GFP_KERNEL); + if (!xscaler) + return -ENOMEM; + + xscaler->xvip.dev = &pdev->dev; + + match = of_match_node(xscaler_of_id_table, node); + if (!match) + return -ENODEV; + + if (!strncmp(match->compatible, xscaler_of_id_table[0].compatible, + strlen(xscaler_of_id_table[0].compatible))) { + dev_warn(&pdev->dev, + "%s - compatible string is getting deprecated!\n", + match->compatible); + } + + xscaler->cfg = match->data; + + ret = xscaler_parse_of(xscaler); + if (ret < 0) + return ret; + + /* Initialize coefficient parameters */ + xscaler->max_num_phases = XSCALER_MAX_PHASES; + + if (xscaler->cfg->flags & XSCALER_CLK_PROP) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xscaler->xvip.iomem = devm_ioremap_resource(xscaler->xvip.dev, + res); + if (IS_ERR(xscaler->xvip.iomem)) + return PTR_ERR(xscaler->xvip.iomem); + + ret = clk_prepare_enable(xscaler->aclk_axis); + if (ret) { + dev_err(&pdev->dev, "failed to enable aclk_axis (%d)\n", + ret); + goto res_cleanup; + } + + ret = clk_prepare_enable(xscaler->aclk_ctrl); + if (ret) { + dev_err(&pdev->dev, "failed to enable aclk_ctrl (%d)\n", + ret); + goto axis_clk_cleanup; + } + } else { + ret = xvip_init_resources(&xscaler->xvip); + if (ret < 0) + return ret; + } + + /* Reset the Global IP Reset through a PS GPIO */ + gpiod_set_value_cansleep(xscaler->rst_gpio, XSCALER_RESET_DEASSERT); + /* Reset internal GPIO within the IP */ + xscaler_reset(xscaler); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &xscaler->xvip.subdev; + v4l2_subdev_init(subdev, &xscaler_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &xscaler_internal_ops; + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, xscaler); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Initialize default and active formats */ + default_format = &xscaler->default_formats[XVIP_PAD_SINK]; + default_format->code = xscaler->vip_formats[XVIP_PAD_SINK]->code; + default_format->field = V4L2_FIELD_NONE; + default_format->colorspace = V4L2_COLORSPACE_SRGB; + default_format->width = XSCALER_DEF_IN_WIDTH; + default_format->height = XSCALER_DEF_IN_HEIGHT; + xscaler->formats[XVIP_PAD_SINK] = *default_format; + + default_format = &xscaler->default_formats[XVIP_PAD_SOURCE]; + *default_format = xscaler->default_formats[XVIP_PAD_SINK]; + default_format->code = xscaler->vip_formats[XVIP_PAD_SOURCE]->code; + default_format->width = XSCALER_DEF_OUT_WIDTH; + default_format->height = XSCALER_DEF_OUT_HEIGHT; + xscaler->formats[XVIP_PAD_SOURCE] = *default_format; + + xscaler->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + xscaler->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &xscaler_media_ops; + + ret = media_entity_pads_init(&subdev->entity, 2, xscaler->pads); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, xscaler); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev"); + goto error; + } + dev_info(xscaler->xvip.dev, "Num Hori Taps %d", + xscaler->num_hori_taps); + dev_info(xscaler->xvip.dev, "Num Vert Taps %d", + xscaler->num_vert_taps); + dev_info(&pdev->dev, "VPSS Scaler Probe Successful"); + return 0; + +error: + media_entity_cleanup(&subdev->entity); + clk_disable_unprepare(xscaler->aclk_ctrl); +axis_clk_cleanup: + clk_disable_unprepare(xscaler->aclk_axis); +res_cleanup: + xvip_cleanup_resources(&xscaler->xvip); + return ret; +} + +static int xscaler_remove(struct platform_device *pdev) +{ + struct xscaler_device *xscaler = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &xscaler->xvip.subdev; + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + clk_disable_unprepare(xscaler->aclk_ctrl); + clk_disable_unprepare(xscaler->aclk_axis); + xvip_cleanup_resources(&xscaler->xvip); + + return 0; +} + +static struct platform_driver xscaler_driver = { + .driver = { + .name = "xilinx-vpss-scaler", + .of_match_table = xscaler_of_id_table, + }, + .probe = xscaler_probe, + .remove = xscaler_remove, +}; + +module_platform_driver(xscaler_driver); +MODULE_DESCRIPTION("Xilinx Scaler VPSS Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/xilinx/xilinx-vtc.c b/drivers/media/platform/xilinx/xilinx-vtc.c index 0ae0208d7529..d69f48dfdac4 100644 --- a/drivers/media/platform/xilinx/xilinx-vtc.c +++ b/drivers/media/platform/xilinx/xilinx-vtc.c @@ -141,6 +141,9 @@ #define XVTC_GENERATOR_GLOBAL_DELAY 0x0104 +/* Value of 1 = .01% */ +#define XVTC_CLK_MAX_PCT_ERR 1 + /** * struct xvtc_device - Xilinx Video Timing Controller device structure * @xvip: Xilinx Video IP device @@ -175,10 +178,25 @@ int xvtc_generator_start(struct xvtc_device *xvtc, const struct xvtc_config *config) { int ret; + unsigned long s_rate; + unsigned long g_rate; + unsigned long clk_err; if (!xvtc->has_generator) return -ENXIO; + s_rate = config->fps * config->hsize * config->vsize; + ret = clk_set_rate(xvtc->xvip.clk, s_rate); + if (ret < 0) + return ret; + + /* Verify that the clock is within a reasonable tolerance. */ + g_rate = clk_get_rate(xvtc->xvip.clk); + clk_err = (abs(g_rate - s_rate) * 10000) / (s_rate); + if (clk_err > XVTC_CLK_MAX_PCT_ERR) + dev_warn(xvtc->xvip.dev, "Failed to set clk rate: %lu, actual rate: %lu\n", + s_rate, g_rate); + ret = clk_prepare_enable(xvtc->xvip.clk); if (ret < 0) return ret; diff --git a/drivers/media/platform/xilinx/xilinx-vtc.h b/drivers/media/platform/xilinx/xilinx-vtc.h index 90cf44245283..c2c744b03426 100644 --- a/drivers/media/platform/xilinx/xilinx-vtc.h +++ b/drivers/media/platform/xilinx/xilinx-vtc.h @@ -27,6 +27,7 @@ struct xvtc_config { unsigned int vsync_start; unsigned int vsync_end; unsigned int vsize; + unsigned int fps; }; struct xvtc_device *xvtc_of_get(struct device_node *np); |