162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Xilinx Video IP Core 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013-2015 Ideas on Board 662306a36Sopenharmony_ci * Copyright (C) 2013-2015 Xilinx, Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> 962306a36Sopenharmony_ci * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <dt-bindings/media/xilinx-vip.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "xilinx-vip.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 2362306a36Sopenharmony_ci * Helper functions 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const struct xvip_video_format xvip_video_formats[] = { 2762306a36Sopenharmony_ci { XVIP_VF_YUV_422, 8, NULL, MEDIA_BUS_FMT_UYVY8_1X16, 2862306a36Sopenharmony_ci 2, V4L2_PIX_FMT_YUYV }, 2962306a36Sopenharmony_ci { XVIP_VF_YUV_444, 8, NULL, MEDIA_BUS_FMT_VUY8_1X24, 3062306a36Sopenharmony_ci 3, V4L2_PIX_FMT_YUV444 }, 3162306a36Sopenharmony_ci { XVIP_VF_RBG, 8, NULL, MEDIA_BUS_FMT_RBG888_1X24, 3262306a36Sopenharmony_ci 3, 0 }, 3362306a36Sopenharmony_ci { XVIP_VF_MONO_SENSOR, 8, "mono", MEDIA_BUS_FMT_Y8_1X8, 3462306a36Sopenharmony_ci 1, V4L2_PIX_FMT_GREY }, 3562306a36Sopenharmony_ci { XVIP_VF_MONO_SENSOR, 8, "rggb", MEDIA_BUS_FMT_SRGGB8_1X8, 3662306a36Sopenharmony_ci 1, V4L2_PIX_FMT_SRGGB8 }, 3762306a36Sopenharmony_ci { XVIP_VF_MONO_SENSOR, 8, "grbg", MEDIA_BUS_FMT_SGRBG8_1X8, 3862306a36Sopenharmony_ci 1, V4L2_PIX_FMT_SGRBG8 }, 3962306a36Sopenharmony_ci { XVIP_VF_MONO_SENSOR, 8, "gbrg", MEDIA_BUS_FMT_SGBRG8_1X8, 4062306a36Sopenharmony_ci 1, V4L2_PIX_FMT_SGBRG8 }, 4162306a36Sopenharmony_ci { XVIP_VF_MONO_SENSOR, 8, "bggr", MEDIA_BUS_FMT_SBGGR8_1X8, 4262306a36Sopenharmony_ci 1, V4L2_PIX_FMT_SBGGR8 }, 4362306a36Sopenharmony_ci { XVIP_VF_MONO_SENSOR, 12, "mono", MEDIA_BUS_FMT_Y12_1X12, 4462306a36Sopenharmony_ci 2, V4L2_PIX_FMT_Y12 }, 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * xvip_get_format_by_code - Retrieve format information for a media bus code 4962306a36Sopenharmony_ci * @code: the format media bus code 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * Return: a pointer to the format information structure corresponding to the 5262306a36Sopenharmony_ci * given V4L2 media bus format @code, or ERR_PTR if no corresponding format can 5362306a36Sopenharmony_ci * be found. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ciconst struct xvip_video_format *xvip_get_format_by_code(unsigned int code) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci unsigned int i; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) { 6062306a36Sopenharmony_ci const struct xvip_video_format *format = &xvip_video_formats[i]; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (format->code == code) 6362306a36Sopenharmony_ci return format; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xvip_get_format_by_code); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/** 7162306a36Sopenharmony_ci * xvip_get_format_by_fourcc - Retrieve format information for a 4CC 7262306a36Sopenharmony_ci * @fourcc: the format 4CC 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * Return: a pointer to the format information structure corresponding to the 7562306a36Sopenharmony_ci * given V4L2 format @fourcc. If not found, return a pointer to the first 7662306a36Sopenharmony_ci * available format (V4L2_PIX_FMT_YUYV). 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ciconst struct xvip_video_format *xvip_get_format_by_fourcc(u32 fourcc) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci unsigned int i; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) { 8362306a36Sopenharmony_ci const struct xvip_video_format *format = &xvip_video_formats[i]; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (format->fourcc == fourcc) 8662306a36Sopenharmony_ci return format; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return &xvip_video_formats[0]; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xvip_get_format_by_fourcc); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/** 9462306a36Sopenharmony_ci * xvip_of_get_format - Parse a device tree node and return format information 9562306a36Sopenharmony_ci * @node: the device tree node 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * Read the xlnx,video-format, xlnx,video-width and xlnx,cfa-pattern properties 9862306a36Sopenharmony_ci * from the device tree @node passed as an argument and return the corresponding 9962306a36Sopenharmony_ci * format information. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Return: a pointer to the format information structure corresponding to the 10262306a36Sopenharmony_ci * format name and width, or ERR_PTR if no corresponding format can be found. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ciconst struct xvip_video_format *xvip_of_get_format(struct device_node *node) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci const char *pattern = "mono"; 10762306a36Sopenharmony_ci unsigned int vf_code; 10862306a36Sopenharmony_ci unsigned int i; 10962306a36Sopenharmony_ci u32 width; 11062306a36Sopenharmony_ci int ret; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ret = of_property_read_u32(node, "xlnx,video-format", &vf_code); 11362306a36Sopenharmony_ci if (ret < 0) 11462306a36Sopenharmony_ci return ERR_PTR(ret); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci ret = of_property_read_u32(node, "xlnx,video-width", &width); 11762306a36Sopenharmony_ci if (ret < 0) 11862306a36Sopenharmony_ci return ERR_PTR(ret); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (vf_code == XVIP_VF_MONO_SENSOR) 12162306a36Sopenharmony_ci of_property_read_string(node, "xlnx,cfa-pattern", &pattern); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xvip_video_formats); ++i) { 12462306a36Sopenharmony_ci const struct xvip_video_format *format = &xvip_video_formats[i]; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (format->vf_code != vf_code || format->width != width) 12762306a36Sopenharmony_ci continue; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (vf_code == XVIP_VF_MONO_SENSOR && 13062306a36Sopenharmony_ci strcmp(pattern, format->pattern)) 13162306a36Sopenharmony_ci continue; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return format; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xvip_of_get_format); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/** 14162306a36Sopenharmony_ci * xvip_set_format_size - Set the media bus frame format size 14262306a36Sopenharmony_ci * @format: V4L2 frame format on media bus 14362306a36Sopenharmony_ci * @fmt: media bus format 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * Set the media bus frame format size. The width / height from the subdevice 14662306a36Sopenharmony_ci * format are set to the given media bus format. The new format size is stored 14762306a36Sopenharmony_ci * in @format. The width and height are clamped using default min / max values. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_civoid xvip_set_format_size(struct v4l2_mbus_framefmt *format, 15062306a36Sopenharmony_ci const struct v4l2_subdev_format *fmt) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci format->width = clamp_t(unsigned int, fmt->format.width, 15362306a36Sopenharmony_ci XVIP_MIN_WIDTH, XVIP_MAX_WIDTH); 15462306a36Sopenharmony_ci format->height = clamp_t(unsigned int, fmt->format.height, 15562306a36Sopenharmony_ci XVIP_MIN_HEIGHT, XVIP_MAX_HEIGHT); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xvip_set_format_size); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/** 16062306a36Sopenharmony_ci * xvip_clr_or_set - Clear or set the register with a bitmask 16162306a36Sopenharmony_ci * @xvip: Xilinx Video IP device 16262306a36Sopenharmony_ci * @addr: address of register 16362306a36Sopenharmony_ci * @mask: bitmask to be set or cleared 16462306a36Sopenharmony_ci * @set: boolean flag indicating whether to set or clear 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * Clear or set the register at address @addr with a bitmask @mask depending on 16762306a36Sopenharmony_ci * the boolean flag @set. When the flag @set is true, the bitmask is set in 16862306a36Sopenharmony_ci * the register, otherwise the bitmask is cleared from the register 16962306a36Sopenharmony_ci * when the flag @set is false. 17062306a36Sopenharmony_ci * 17162306a36Sopenharmony_ci * Fox example, this function can be used to set a control with a boolean value 17262306a36Sopenharmony_ci * requested by users. If the caller knows whether to set or clear in the first 17362306a36Sopenharmony_ci * place, the caller should call xvip_clr() or xvip_set() directly instead of 17462306a36Sopenharmony_ci * using this function. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_civoid xvip_clr_or_set(struct xvip_device *xvip, u32 addr, u32 mask, bool set) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci u32 reg; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci reg = xvip_read(xvip, addr); 18162306a36Sopenharmony_ci reg = set ? reg | mask : reg & ~mask; 18262306a36Sopenharmony_ci xvip_write(xvip, addr, reg); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xvip_clr_or_set); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/** 18762306a36Sopenharmony_ci * xvip_clr_and_set - Clear and set the register with a bitmask 18862306a36Sopenharmony_ci * @xvip: Xilinx Video IP device 18962306a36Sopenharmony_ci * @addr: address of register 19062306a36Sopenharmony_ci * @clr: bitmask to be cleared 19162306a36Sopenharmony_ci * @set: bitmask to be set 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * Clear a bit(s) of mask @clr in the register at address @addr, then set 19462306a36Sopenharmony_ci * a bit(s) of mask @set in the register after. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_civoid xvip_clr_and_set(struct xvip_device *xvip, u32 addr, u32 clr, u32 set) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci u32 reg; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci reg = xvip_read(xvip, addr); 20162306a36Sopenharmony_ci reg &= ~clr; 20262306a36Sopenharmony_ci reg |= set; 20362306a36Sopenharmony_ci xvip_write(xvip, addr, reg); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xvip_clr_and_set); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciint xvip_init_resources(struct xvip_device *xvip) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(xvip->dev); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci xvip->iomem = devm_platform_ioremap_resource(pdev, 0); 21262306a36Sopenharmony_ci if (IS_ERR(xvip->iomem)) 21362306a36Sopenharmony_ci return PTR_ERR(xvip->iomem); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci xvip->clk = devm_clk_get(xvip->dev, NULL); 21662306a36Sopenharmony_ci if (IS_ERR(xvip->clk)) 21762306a36Sopenharmony_ci return PTR_ERR(xvip->clk); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci clk_prepare_enable(xvip->clk); 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xvip_init_resources); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_civoid xvip_cleanup_resources(struct xvip_device *xvip) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci clk_disable_unprepare(xvip->clk); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xvip_cleanup_resources); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 23162306a36Sopenharmony_ci * Subdev operations handlers 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/** 23562306a36Sopenharmony_ci * xvip_enum_mbus_code - Enumerate the media format code 23662306a36Sopenharmony_ci * @subdev: V4L2 subdevice 23762306a36Sopenharmony_ci * @sd_state: V4L2 subdev state 23862306a36Sopenharmony_ci * @code: returning media bus code 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * Enumerate the media bus code of the subdevice. Return the corresponding 24162306a36Sopenharmony_ci * pad format code. This function only works for subdevices with fixed format 24262306a36Sopenharmony_ci * on all pads. Subdevices with multiple format should have their own 24362306a36Sopenharmony_ci * function to enumerate mbus codes. 24462306a36Sopenharmony_ci * 24562306a36Sopenharmony_ci * Return: 0 if the media bus code is found, or -EINVAL if the format index 24662306a36Sopenharmony_ci * is not valid. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ciint xvip_enum_mbus_code(struct v4l2_subdev *subdev, 24962306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 25062306a36Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct v4l2_mbus_framefmt *format; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Enumerating frame sizes based on the active configuration isn't 25562306a36Sopenharmony_ci * supported yet. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci if (code->which == V4L2_SUBDEV_FORMAT_ACTIVE) 25862306a36Sopenharmony_ci return -EINVAL; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (code->index) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci format = v4l2_subdev_get_try_format(subdev, sd_state, code->pad); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci code->code = format->code; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xvip_enum_mbus_code); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/** 27262306a36Sopenharmony_ci * xvip_enum_frame_size - Enumerate the media bus frame size 27362306a36Sopenharmony_ci * @subdev: V4L2 subdevice 27462306a36Sopenharmony_ci * @sd_state: V4L2 subdev state 27562306a36Sopenharmony_ci * @fse: returning media bus frame size 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * This function is a drop-in implementation of the subdev enum_frame_size pad 27862306a36Sopenharmony_ci * operation. It assumes that the subdevice has one sink pad and one source 27962306a36Sopenharmony_ci * pad, and that the format on the source pad is always identical to the 28062306a36Sopenharmony_ci * format on the sink pad. Entities with different requirements need to 28162306a36Sopenharmony_ci * implement their own enum_frame_size handlers. 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * Return: 0 if the media bus frame size is found, or -EINVAL 28462306a36Sopenharmony_ci * if the index or the code is not valid. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ciint xvip_enum_frame_size(struct v4l2_subdev *subdev, 28762306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 28862306a36Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct v4l2_mbus_framefmt *format; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Enumerating frame sizes based on the active configuration isn't 29362306a36Sopenharmony_ci * supported yet. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (fse->which == V4L2_SUBDEV_FORMAT_ACTIVE) 29662306a36Sopenharmony_ci return -EINVAL; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (fse->index || fse->code != format->code) 30162306a36Sopenharmony_ci return -EINVAL; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (fse->pad == XVIP_PAD_SINK) { 30462306a36Sopenharmony_ci fse->min_width = XVIP_MIN_WIDTH; 30562306a36Sopenharmony_ci fse->max_width = XVIP_MAX_WIDTH; 30662306a36Sopenharmony_ci fse->min_height = XVIP_MIN_HEIGHT; 30762306a36Sopenharmony_ci fse->max_height = XVIP_MAX_HEIGHT; 30862306a36Sopenharmony_ci } else { 30962306a36Sopenharmony_ci /* The size on the source pad is fixed and always identical to 31062306a36Sopenharmony_ci * the size on the sink pad. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci fse->min_width = format->width; 31362306a36Sopenharmony_ci fse->max_width = format->width; 31462306a36Sopenharmony_ci fse->min_height = format->height; 31562306a36Sopenharmony_ci fse->max_height = format->height; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xvip_enum_frame_size); 321