From patchwork Wed Jan 11 02:24:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuji Ishikawa X-Patchwork-Id: 641549 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 22418C677F1 for ; Wed, 11 Jan 2023 02:31:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235817AbjAKCb3 (ORCPT ); Tue, 10 Jan 2023 21:31:29 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42752 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235801AbjAKCaw (ORCPT ); Tue, 10 Jan 2023 21:30:52 -0500 Received: from mo-csw.securemx.jp (mo-csw1114.securemx.jp [210.130.202.156]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A5B666473; Tue, 10 Jan 2023 18:30:47 -0800 (PST) Received: by mo-csw.securemx.jp (mx-mo-csw1114) id 30B2U45l021438; Wed, 11 Jan 2023 11:30:04 +0900 X-Iguazu-Qid: 2wGqnqi0Hu8S3SBcxM X-Iguazu-QSIG: v=2; s=0; t=1673404204; q=2wGqnqi0Hu8S3SBcxM; m=3rACQVTc/Td/iyF3BbBaPbZ2C0wFxZHiohWkLdce4Z4= Received: from imx2-a.toshiba.co.jp (imx2-a.toshiba.co.jp [106.186.93.35]) by relay.securemx.jp (mx-mr1111) id 30B2U1Ue020677 (version=TLSv1.2 cipher=AES128-GCM-SHA256 bits=128 verify=NOT); Wed, 11 Jan 2023 11:30:02 +0900 X-SA-MID: 52825685 From: Yuji Ishikawa To: Hans Verkuil , Laurent Pinchart , Mauro Carvalho Chehab , Nobuhiro Iwamatsu , Rob Herring , Krzysztof Kozlowski , "Rafael J . Wysocki" , Mark Brown Cc: linux-media@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, yuji2.ishikawa@toshiba.co.jp Subject: [PATCH v5 3/6] media: platform: visconti: Add Toshiba Visconti Video Input Interface driver user interace Date: Wed, 11 Jan 2023 11:24:30 +0900 X-TSB-HOP2: ON Message-Id: <20230111022433.25950-4-yuji2.ishikawa@toshiba.co.jp> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230111022433.25950-1-yuji2.ishikawa@toshiba.co.jp> References: <20230111022433.25950-1-yuji2.ishikawa@toshiba.co.jp> Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add support to Video Input Interface on Toshiba Visconti ARM SoCs. The interface device includes CSI2 Receiver, frame grabber, video DMAC and image signal processor. This patch provides the user interface layer. A driver instance provides three /dev/videoX device files; one for RGB image capture, another one for optional RGB capture with different parameters and the last one for RAW capture. Through the device files, the driver provides streaming (DMA-BUF) interface. A userland application should feed DMA-BUF instances for capture buffers. The driver is based on media controller framework. Its operations are roughly mapped to two subdrivers; one for ISP and CSI2 receiver (yields 1 instance), the other for capture (yields 3 instances for each capture mode). Signed-off-by: Yuji Ishikawa --- Changelog v2: - Resend v1 because a patch exceeds size limit. Changelog v3: - Adapted to media control framework - Introduced ISP subdevice, capture device - Remove private IOCTLs and add vendor specific V4L2 controls - Change function name avoiding camelcase and uppercase letters Changelog v4: - Split patches because the v3 patch exceeds size limit - Stop using ID number to identify driver instance: - Use dynamically allocated structure to hold HW specific context, instead of static one. - Call HW layer functions with the context structure instead of ID number - Use pm_runtime to trigger initialization of HW along with open/close of device files. Changelog v5: - Fix coding style problems in viif.c --- drivers/media/platform/visconti/Makefile | 1 + drivers/media/platform/visconti/viif.c | 545 ++++++++ drivers/media/platform/visconti/viif.h | 203 +++ .../media/platform/visconti/viif_capture.c | 1201 +++++++++++++++++ drivers/media/platform/visconti/viif_isp.c | 846 ++++++++++++ 5 files changed, 2796 insertions(+) create mode 100644 drivers/media/platform/visconti/viif.c create mode 100644 drivers/media/platform/visconti/viif.h create mode 100644 drivers/media/platform/visconti/viif_capture.c create mode 100644 drivers/media/platform/visconti/viif_isp.c diff --git a/drivers/media/platform/visconti/Makefile b/drivers/media/platform/visconti/Makefile index e14b904df75..d7a23c1f4e8 100644 --- a/drivers/media/platform/visconti/Makefile +++ b/drivers/media/platform/visconti/Makefile @@ -3,6 +3,7 @@ # Makefile for the Visconti video input device driver # +visconti-viif-objs = viif.o viif_capture.o viif_isp.o visconti-viif-objs += hwd_viif_csi2rx.o hwd_viif.o obj-$(CONFIG_VIDEO_VISCONTI_VIIF) += visconti-viif.o diff --git a/drivers/media/platform/visconti/viif.c b/drivers/media/platform/visconti/viif.c new file mode 100644 index 00000000000..e29480dbb76 --- /dev/null +++ b/drivers/media/platform/visconti/viif.c @@ -0,0 +1,545 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2022 TOSHIBA CORPORATION + * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "viif.h" + +static inline struct viif_device *v4l2_to_viif(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct viif_device, v4l2_dev); +} + +static struct viif_subdev *to_viif_subdev(struct v4l2_async_subdev *asd) +{ + return container_of(asd, struct viif_subdev, asd); +} + +/* VSYNC mask setting of MAIN unit */ +#define INT_M_SYNC_MASK_VSYNC_INT BIT(0) +#define INT_M_SYNC_MASK_LINES_DELAY_INT1 BIT(1) +#define INT_M_SYNC_MASK_LINES_DELAY_INT2 BIT(2) +#define INT_M_SYNC_MASK_SW_DELAY_INT0 BIT(16) +#define INT_M_SYNC_MASK_SW_DELAY_INT1 BIT(17) +#define INT_M_SYNC_MASK_SW_DELAY_INT2 BIT(18) + +/* STATUS error mask setting of MAIN unit */ +#define INT_M_MASK_L2ISP_SIZE_ERROR BIT(0) +#define INT_M_MASK_CRGBF_INTCRGERR_WRSTART BIT(1) +#define INT_M_MASK_CRGBF_INTCRGERR_RDSTART BIT(2) +#define INT_M_MASK_EMBED_ERROR BIT(3) +#define INT_M_MASK_USERDATA_ERROR BIT(4) +#define INT_M_MASK_L2ISP_POST0_TABLE_TIMEOUT BIT(8) +#define INT_M_MASK_L2ISP_POST1_TABLE_TIMEOUT BIT(9) +#define INT_M_MASK_L2ISP_GRID_TABLE_TIMEOUT BIT(11) +#define INT_M_MASK_L1ISP_SIZE_ERROR0 BIT(16) +#define INT_M_MASK_L1ISP_SIZE_ERROR1 BIT(17) +#define INT_M_MASK_L1ISP_SIZE_ERROR2 BIT(18) +#define INT_M_MASK_L1ISP_SIZE_ERROR3 BIT(19) +#define INT_M_MASK_L1ISP_SIZE_ERROR4 BIT(20) +#define INT_M_MASK_L1ISP_INT_ERR_CRGWRSTART BIT(21) +#define INT_M_MASK_L1ISP_INT_ERR_CRGRDSTART BIT(22) +#define INT_M_MASK_DELAY_INT_ERROR BIT(24) + +/* VSYNC mask settings of SUB unit */ +#define INT_S_SYNC_MASK_VSYNC_INT BIT(0) +#define INT_S_SYNC_MASK_LINES_DELAY_INT1 BIT(1) +#define INT_S_SYNC_MASK_SW_DELAY_INT0 BIT(16) +#define INT_S_SYNC_MASK_SW_DELAY_INT1 BIT(17) + +/* STATUS error mask setting of SUB unit */ +#define INT_S_MASK_SIZE_ERROR BIT(0) +#define INT_S_MASK_EMBED_ERROR BIT(1) +#define INT_S_MASK_USERDATA_ERROR BIT(2) +#define INT_S_MASK_DELAY_INT_ERROR BIT(24) +#define INT_S_MASK_RESERVED_SET (BIT(16) | BIT(28)) + +static void viif_vsync_irq_handler_w_isp(struct viif_device *viif_dev) +{ + u32 event_main, event_sub, status_err, l2_transfer_status; + u64 ts; + + ts = ktime_get_ns(); + hwd_viif_vsync_irq_handler(viif_dev->hwd_res, &event_main, &event_sub); + + /* Delayed Vsync of MAIN unit */ + if (event_main & INT_M_SYNC_MASK_LINES_DELAY_INT2) { + /* unmask timeout error of gamma table */ + hwd_viif_main_status_err_set_irq_mask(viif_dev->hwd_res, + INT_M_MASK_DELAY_INT_ERROR); + viif_dev->masked_gamma_path = 0; + + /* Get abort status of L2ISP */ + hwd_viif_isp_guard_start(viif_dev->hwd_res); + hwd_viif_isp_get_info(viif_dev->hwd_res, NULL, &l2_transfer_status); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + + status_err = viif_dev->status_err; + viif_dev->status_err = 0; + + visconti_viif_capture_switch_buffer(&viif_dev->cap_dev0, status_err, + l2_transfer_status, ts); + visconti_viif_capture_switch_buffer(&viif_dev->cap_dev1, status_err, + l2_transfer_status, ts); + } + + /* Delayed Vsync of SUB unit */ + if (event_sub & INT_S_SYNC_MASK_LINES_DELAY_INT1) + visconti_viif_capture_switch_buffer(&viif_dev->cap_dev2, 0, 0, ts); +} + +#define MASK_M_GAMMATBL_TIMEOUT 0x0700U + +static void viif_status_err_irq_handler(struct viif_device *viif_dev) +{ + u32 event_main, event_sub, val, mask; + + hwd_viif_status_err_irq_handler(viif_dev->hwd_res, &event_main, &event_sub); + + if (event_main) { + /* mask for gamma table time out error which will be unmasked in the next Vsync */ + val = FIELD_GET(MASK_M_GAMMATBL_TIMEOUT, event_main); + if (val) { + viif_dev->masked_gamma_path |= val; + mask = INT_M_MASK_DELAY_INT_ERROR | + FIELD_PREP(MASK_M_GAMMATBL_TIMEOUT, viif_dev->masked_gamma_path); + hwd_viif_main_status_err_set_irq_mask(viif_dev->hwd_res, mask); + } + + viif_dev->status_err = event_main; + } + viif_dev->reported_err_main |= event_main; + viif_dev->reported_err_sub |= event_sub; + dev_err(viif_dev->dev, "MAIN/SUB error 0x%x 0x%x.\n", event_main, event_sub); +} + +static void viif_csi2rx_err_irq_handler(struct viif_device *viif_dev) +{ + u32 event; + + event = hwd_viif_csi2rx_err_irq_handler(viif_dev->hwd_res); + viif_dev->reported_err_csi2rx |= event; + dev_err(viif_dev->dev, "CSI2RX error 0x%x.\n", event); +} + +static irqreturn_t visconti_viif_irq(int irq, void *dev_id) +{ + struct viif_device *viif_dev = dev_id; + int irq_type = irq - viif_dev->irq[0]; + + spin_lock(&viif_dev->lock); + + switch (irq_type) { + case 0: + viif_vsync_irq_handler_w_isp(viif_dev); + break; + case 1: + viif_status_err_irq_handler(viif_dev); + break; + case 2: + viif_csi2rx_err_irq_handler(viif_dev); + break; + } + + spin_unlock(&viif_dev->lock); + + return IRQ_HANDLED; +} + +/* ----- Async Notifier Operations----- */ +static int visconti_viif_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *v4l2_sd, struct v4l2_async_subdev *asd) +{ + struct v4l2_device *v4l2_dev = notifier->v4l2_dev; + struct viif_device *viif_dev = v4l2_to_viif(v4l2_dev); + struct viif_subdev *viif_sd = to_viif_subdev(asd); + + viif_sd->v4l2_sd = v4l2_sd; + viif_dev->num_sd++; + + return 0; +} + +static void visconti_viif_create_links(struct viif_device *viif_dev) +{ + unsigned int source_pad; + int ret; + + /* camera subdev pad0 -> isp suddev pad0 */ + ret = media_entity_get_fwnode_pad(&viif_dev->sd->v4l2_sd->entity, + viif_dev->sd->v4l2_sd->fwnode, MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(viif_dev->dev, "failed to find source pad\n"); + return; + } + source_pad = ret; + + ret = media_create_pad_link(&viif_dev->sd->v4l2_sd->entity, source_pad, + &viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(viif_dev->dev, "failed create_pad_link (camera:src -> isp:sink)\n"); + + ret = media_create_pad_link(&viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SRC_PATH0, + &viif_dev->cap_dev0.vdev.entity, VIIF_CAPTURE_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(viif_dev->dev, "failed create_pad_link (isp:src -> capture0:sink)\n"); + + ret = media_create_pad_link(&viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SRC_PATH1, + &viif_dev->cap_dev1.vdev.entity, VIIF_CAPTURE_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(viif_dev->dev, "failed create_pad_link (isp:src -> capture1:sink)\n"); + + ret = media_create_pad_link(&viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SRC_PATH2, + &viif_dev->cap_dev2.vdev.entity, VIIF_CAPTURE_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(viif_dev->dev, "failed create_pad_link (isp:src -> capture2:sink)\n"); +} + +static void visconti_viif_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) +{ + struct v4l2_device *v4l2_dev = notifier->v4l2_dev; + struct viif_subdev *viif_sd = to_viif_subdev(asd); + + v4l2_dev->ctrl_handler = NULL; + viif_sd->v4l2_sd = NULL; +} + +static int visconti_viif_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct v4l2_device *v4l2_dev = notifier->v4l2_dev; + struct viif_device *viif_dev = v4l2_to_viif(v4l2_dev); + int ret; + + ret = v4l2_device_register_subdev_nodes(v4l2_dev); + if (ret < 0) { + dev_err(v4l2_dev->dev, "Failed to register subdev nodes\n"); + return ret; + } + + /* Make sure at least one sensor is primary and use it to initialize */ + if (!viif_dev->sd) { + viif_dev->sd = &viif_dev->subdevs[0]; + viif_dev->sd_index = 0; + } + + ret = visconti_viif_capture_register_ctrl_handlers(viif_dev); + if (ret) + return ret; + + visconti_viif_create_links(viif_dev); + + return 0; +} + +static const struct v4l2_async_notifier_operations viif_notify_ops = { + .bound = visconti_viif_notify_bound, + .unbind = visconti_viif_notify_unbind, + .complete = visconti_viif_notify_complete, +}; + +/* ----- Probe and Remove ----- */ +static int visconti_viif_init_async_subdevs(struct viif_device *viif_dev, unsigned int n_sd) +{ + /* Reserve memory for 'n_sd' viif_subdev descriptors. */ + viif_dev->subdevs = + devm_kcalloc(viif_dev->dev, n_sd, sizeof(*viif_dev->subdevs), GFP_KERNEL); + if (!viif_dev->subdevs) + return -ENOMEM; + + /* Reserve memory for 'n_sd' pointers to async_subdevices. + * viif_dev->asds members will point to &viif_dev.asd + */ + viif_dev->asds = devm_kcalloc(viif_dev->dev, n_sd, sizeof(*viif_dev->asds), GFP_KERNEL); + if (!viif_dev->asds) + return -ENOMEM; + + viif_dev->sd = NULL; + viif_dev->sd_index = 0; + viif_dev->num_sd = 0; + + return 0; +} + +static int visconti_viif_parse_dt(struct viif_device *viif_dev) +{ + struct device_node *of = viif_dev->dev->of_node; + struct v4l2_fwnode_endpoint fw_ep; + struct viif_subdev *viif_sd; + struct device_node *ep; + unsigned int i; + int num_ep; + int ret; + + memset(&fw_ep, 0, sizeof(struct v4l2_fwnode_endpoint)); + + num_ep = of_graph_get_endpoint_count(of); + if (!num_ep) + return -ENODEV; + + ret = visconti_viif_init_async_subdevs(viif_dev, num_ep); + if (ret) + return ret; + + for (i = 0; i < num_ep; i++) { + ep = of_graph_get_endpoint_by_regs(of, 0, i); + if (!ep) { + dev_err(viif_dev->dev, "No subdevice connected on endpoint %u.\n", i); + ret = -ENODEV; + goto error_put_node; + } + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &fw_ep); + if (ret) { + dev_err(viif_dev->dev, "Unable to parse endpoint #%u.\n", i); + goto error_put_node; + } + + if (fw_ep.bus_type != V4L2_MBUS_CSI2_DPHY || + fw_ep.bus.mipi_csi2.num_data_lanes == 0) { + dev_err(viif_dev->dev, "missing CSI-2 properties in endpoint\n"); + ret = -EINVAL; + goto error_put_node; + } + + /* Setup the ceu subdevice and the async subdevice. */ + viif_sd = &viif_dev->subdevs[i]; + INIT_LIST_HEAD(&viif_sd->asd.list); + + viif_sd->mbus_flags = fw_ep.bus.mipi_csi2.flags; + viif_sd->num_lane = fw_ep.bus.mipi_csi2.num_data_lanes; + viif_sd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + viif_sd->asd.match.fwnode = + fwnode_graph_get_remote_port_parent(of_fwnode_handle(ep)); + + viif_dev->asds[i] = &viif_sd->asd; + of_node_put(ep); + } + + return num_ep; + +error_put_node: + of_node_put(ep); + return ret; +} + +static const struct of_device_id visconti_viif_of_table[] = { + { + .compatible = "toshiba,visconti-viif", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, visconti_viif_of_table); + +#define NUM_IRQS 3 +#define IRQ_ID_STR "viif" +#define MEDIA_MODEL "visconti_viif" +#define MEDIA_BUS_INFO "platform:visconti_viif" + +static int visconti_viif_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + struct device *dev = &pdev->dev; + struct viif_device *viif_dev; + dma_addr_t table_paddr; + int ret, i, num_sd; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); + if (ret) + return ret; + + viif_dev = devm_kzalloc(dev, sizeof(*viif_dev), GFP_KERNEL); + if (!viif_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, viif_dev); + viif_dev->dev = dev; + + spin_lock_init(&viif_dev->lock); + mutex_init(&viif_dev->pow_lock); + + viif_dev->capture_reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(viif_dev->capture_reg)) + return PTR_ERR(viif_dev->capture_reg); + + viif_dev->csi2host_reg = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(viif_dev->csi2host_reg)) + return PTR_ERR(viif_dev->csi2host_reg); + + viif_dev->hwd_res = allocate_viif_res(dev, viif_dev->csi2host_reg, viif_dev->capture_reg); + + for (i = 0; i < NUM_IRQS; i++) { + ret = platform_get_irq(pdev, i); + if (ret < 0) { + dev_err(dev, "failed to acquire irq resource\n"); + return ret; + } + viif_dev->irq[i] = ret; + ret = devm_request_irq(dev, viif_dev->irq[i], visconti_viif_irq, 0, IRQ_ID_STR, + viif_dev); + if (ret) { + dev_err(dev, "irq request failed\n"); + return ret; + } + } + + viif_dev->table_vaddr = + dma_alloc_wc(dev, sizeof(struct viif_table_area), &table_paddr, GFP_KERNEL); + if (!viif_dev->table_vaddr) { + dev_err(dev, "dma_alloc_wc failed\n"); + return -ENOMEM; + } + viif_dev->table_paddr = (struct viif_table_area *)table_paddr; + + /* power control */ + pm_runtime_enable(dev); + + /* build media_dev */ + viif_dev->media_dev.hw_revision = 0; + strscpy(viif_dev->media_dev.model, MEDIA_MODEL, sizeof(viif_dev->media_dev.model)); + viif_dev->media_dev.dev = dev; + strscpy(viif_dev->media_dev.bus_info, MEDIA_BUS_INFO, sizeof(viif_dev->media_dev.bus_info)); + media_device_init(&viif_dev->media_dev); + + /* build v4l2_dev */ + viif_dev->v4l2_dev.mdev = &viif_dev->media_dev; + ret = v4l2_device_register(dev, &viif_dev->v4l2_dev); + if (ret) + goto error_dma_free; + + ret = media_device_register(&viif_dev->media_dev); + if (ret) { + dev_err(dev, "Failed to register media device: %d\n", ret); + goto error_v4l2_unregister; + } + + ret = visconti_viif_isp_register(viif_dev); + if (ret) { + dev_err(dev, "failed to register isp sub node: %d\n", ret); + goto error_media_unregister; + } + ret = visconti_viif_capture_register(viif_dev); + if (ret) { + dev_err(dev, "failed to register capture node: %d\n", ret); + goto error_media_unregister; + } + + /* check device type */ + of_id = of_match_device(visconti_viif_of_table, dev); + + num_sd = visconti_viif_parse_dt(viif_dev); + if (ret < 0) { + ret = num_sd; + goto error_media_unregister; + } + + viif_dev->notifier.v4l2_dev = &viif_dev->v4l2_dev; + v4l2_async_nf_init(&viif_dev->notifier); + for (i = 0; i < num_sd; i++) + __v4l2_async_nf_add_subdev(&viif_dev->notifier, viif_dev->asds[i]); + viif_dev->notifier.ops = &viif_notify_ops; + ret = v4l2_async_nf_register(&viif_dev->v4l2_dev, &viif_dev->notifier); + if (ret) + goto error_media_unregister; + + return 0; + +error_media_unregister: + media_device_unregister(&viif_dev->media_dev); +error_v4l2_unregister: + v4l2_device_unregister(&viif_dev->v4l2_dev); +error_dma_free: + pm_runtime_disable(dev); + dma_free_wc(&pdev->dev, sizeof(struct viif_table_area), viif_dev->table_vaddr, + (dma_addr_t)viif_dev->table_paddr); + return ret; +} + +static int visconti_viif_remove(struct platform_device *pdev) +{ + struct viif_device *viif_dev = platform_get_drvdata(pdev); + + visconti_viif_isp_unregister(viif_dev); + visconti_viif_capture_unregister(viif_dev); + v4l2_async_nf_unregister(&viif_dev->notifier); + media_device_unregister(&viif_dev->media_dev); + v4l2_device_unregister(&viif_dev->v4l2_dev); + pm_runtime_disable(&pdev->dev); + dma_free_wc(&pdev->dev, sizeof(struct viif_table_area), viif_dev->table_vaddr, + (dma_addr_t)viif_dev->table_paddr); + + return 0; +} + +static int visconti_viif_runtime_suspend(struct device *dev) +{ + /* This callback is kicked when the last device-file is closed */ + return 0; +} + +static int visconti_viif_runtime_resume(struct device *dev) +{ + /* This callback is kicked when the first device-file is opened */ + struct viif_device *viif_dev = dev_get_drvdata(dev); + + viif_dev->rawpack_mode = HWD_VIIF_RAWPACK_DISABLE; + + mutex_lock(&viif_dev->pow_lock); + + /* VSYNC mask setting of MAIN unit */ + hwd_viif_main_vsync_set_irq_mask( + viif_dev->hwd_res, INT_M_SYNC_MASK_VSYNC_INT | INT_M_SYNC_MASK_LINES_DELAY_INT1 | + INT_M_SYNC_MASK_SW_DELAY_INT0 | + INT_M_SYNC_MASK_SW_DELAY_INT2); + + /* STATUS error mask setting of MAIN unit */ + hwd_viif_main_status_err_set_irq_mask(viif_dev->hwd_res, INT_M_MASK_DELAY_INT_ERROR); + + /* VSYNC mask settings of SUB unit */ + hwd_viif_sub_vsync_set_irq_mask(viif_dev->hwd_res, INT_S_SYNC_MASK_VSYNC_INT | + INT_S_SYNC_MASK_SW_DELAY_INT0 | + INT_S_SYNC_MASK_SW_DELAY_INT1); + + /* STATUS error mask setting(unmask) of SUB unit */ + hwd_viif_sub_status_err_set_irq_mask(viif_dev->hwd_res, + INT_S_MASK_RESERVED_SET | INT_S_MASK_DELAY_INT_ERROR); + + mutex_unlock(&viif_dev->pow_lock); + + return 0; +} + +static const struct dev_pm_ops visconti_viif_pm_ops = { SET_RUNTIME_PM_OPS( + visconti_viif_runtime_suspend, visconti_viif_runtime_resume, NULL) }; + +static struct platform_driver visconti_viif_driver = { + .probe = visconti_viif_probe, + .remove = visconti_viif_remove, + .driver = { + .name = "visconti_viif", + .of_match_table = visconti_viif_of_table, + .pm = &visconti_viif_pm_ops, + }, +}; + +module_platform_driver(visconti_viif_driver); + +MODULE_AUTHOR("Yuji Ishikawa "); +MODULE_DESCRIPTION("Toshiba Visconti Video Input driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/media/platform/visconti/viif.h b/drivers/media/platform/visconti/viif.h new file mode 100644 index 00000000000..cd121ae3200 --- /dev/null +++ b/drivers/media/platform/visconti/viif.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2022 TOSHIBA CORPORATION + * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation + */ + +#ifndef VIIF_H +#define VIIF_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hwd_viif.h" + +#define VIIF_ISP_REGBUF_0 0 +#define VIIF_L2ISP_POST_0 0 +#define VIIF_L2ISP_POST_1 1 + +#define VIIF_CAPTURE_PAD_SINK 0 +#define VIIF_ISP_PAD_SINK 0 +#define VIIF_ISP_PAD_SRC_PATH0 1 +#define VIIF_ISP_PAD_SRC_PATH1 2 +#define VIIF_ISP_PAD_SRC_PATH2 3 +#define VIIF_ISP_PAD_NUM 4 + +#define CAPTURE_PATH_MAIN_POST0 0 +#define CAPTURE_PATH_MAIN_POST1 1 +#define CAPTURE_PATH_SUB 2 + +#define VIIF_DPC_TABLE_BYTES 8192 +#define VIIF_LSC_TABLE_BYTES 1536 +#define VIIF_UNDIST_TABLE_BYTES 8192 +#define VIIF_L2_GAMMA_TABLE_BYTES 512 + +#define VIIF_HW_AVAILABLE_IRQS 4 + +struct viif_fmt { + u32 fourcc; + u8 bpp[3]; + u8 num_planes; + u32 colorspace; + u32 pitch_align; +}; + +struct viif_subdev { + struct v4l2_subdev *v4l2_sd; + struct v4l2_async_subdev asd; + + /* per-subdevice mbus configuration options */ + unsigned int mbus_flags; + unsigned int mbus_code; + unsigned int num_lane; +}; + +struct viif_table_area { + /* viif_l1_dpc_config */ + u32 dpc_table_h[VIIF_DPC_TABLE_BYTES / sizeof(u32)]; + u32 dpc_table_m[VIIF_DPC_TABLE_BYTES / sizeof(u32)]; + u32 dpc_table_l[VIIF_DPC_TABLE_BYTES / sizeof(u32)]; + /* viif_l1_lsc_config */ + u16 lsc_table_gr[VIIF_LSC_TABLE_BYTES / sizeof(u16)]; + u16 lsc_table_r[VIIF_LSC_TABLE_BYTES / sizeof(u16)]; + u16 lsc_table_b[VIIF_LSC_TABLE_BYTES / sizeof(u16)]; + u16 lsc_table_gb[VIIF_LSC_TABLE_BYTES / sizeof(u16)]; + /* viif_l2_undist_config */ + u32 undist_write_g[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)]; + u32 undist_read_b[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)]; + u32 undist_read_g[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)]; + u32 undist_read_r[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)]; + /* viif_l2_gamma_config */ + u16 l2_gamma_table[2][6][VIIF_L2_GAMMA_TABLE_BYTES / sizeof(u16)]; +}; + +/* capture device node information */ +struct cap_dev { + u32 pathid; /* 0 ... MAIN POST0, 1 ... MAIN POST1, 2 ... SUB */ + struct video_device vdev; + struct media_pad capture_pad; + struct v4l2_ctrl_handler ctrl_handler; + struct mutex vlock; /* serialize ioctl to vb2_queue and video_device */ + + /* vb2 queue, capture buffer list and active buffer pointer */ + struct vb2_queue vb2_vq; + struct list_head buf_queue; + struct vb2_v4l2_buffer *active; + struct vb2_v4l2_buffer *dma_active; + int buf_cnt; + unsigned int sequence; + + /* currently configured field and pixel format */ + enum v4l2_field field; + struct v4l2_pix_format_mplane v4l2_pix; + unsigned int out_format; + struct hwd_viif_img_area img_area; + struct hwd_viif_out_process out_process; + + struct viif_device *viif_dev; +}; + +struct isp_subdev { + struct v4l2_subdev sd; + struct media_pad pads[VIIF_ISP_PAD_NUM]; + struct v4l2_subdev_pad_config pad_cfg[VIIF_ISP_PAD_NUM]; + struct mutex ops_lock; /* serialize V4L2 query */ + struct viif_device *viif_dev; + struct v4l2_ctrl_handler ctrl_handler; +}; + +struct hwd_viif_res; + +struct viif_device { + struct device *dev; + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct media_pipeline pipe; + u32 masked_gamma_path; + struct hwd_viif_func *func; + + struct viif_subdev *subdevs; + struct v4l2_async_subdev **asds; + /* async subdev notification helpers */ + struct v4l2_async_notifier notifier; + + /* the subdevice currently in use */ + struct viif_subdev *sd; + unsigned int sd_index; + unsigned int num_sd; + + /* sub device node information */ + struct cap_dev cap_dev0; + struct cap_dev cap_dev1; + struct cap_dev cap_dev2; + struct isp_subdev isp_subdev; + + /* lock - serialize calls to low-level operations (hwd_xxxx) */ + /* also, this serialize access to capture buffer queue and active buffer */ + spinlock_t lock; + + /* pow_lock - serialize power control*/ + struct mutex pow_lock; + + struct { + u32 clock_id; + u32 csi2_clock_id; + u32 csi2_reset_id; + } clk_compat; + + /* hwd_res - context of low level implementation */ + struct hwd_viif_res *hwd_res; + + void __iomem *capture_reg; + void __iomem *csi2host_reg; + unsigned int irq[VIIF_HW_AVAILABLE_IRQS]; + + /* Un-cache table area */ + struct viif_table_area *table_vaddr; + struct viif_table_area *table_paddr; + + /* Rawpack mode */ + u32 rawpack_mode; + + /* Error flag checked at delayed vsync handler */ + u32 status_err; + + /* Error flag checked at compound control GET_REPORTED_ERRORS */ + u32 reported_err_main; + u32 reported_err_sub; + u32 reported_err_csi2rx; +}; + +/* viif.c */ +void visconti_viif_hw_on(struct viif_device *viif_dev); +void visconti_viif_hw_off(struct viif_device *viif_dev); + +/* viif_capture.c */ +int visconti_viif_capture_register(struct viif_device *viif_dev); +void visconti_viif_capture_unregister(struct viif_device *viif_dev); +int visconti_viif_capture_register_ctrl_handlers(struct viif_device *viif_dev); +void visconti_viif_capture_switch_buffer(struct cap_dev *cap_dev, u32 status_err, + u32 l2_transfer_status, u64 timestamp); + +/* viif_isp.c */ +int visconti_viif_isp_register(struct viif_device *viif_dev); +void visconti_viif_isp_unregister(struct viif_device *viif_dev); +int visconti_viif_isp_main_set_unit(struct viif_device *viif_dev); +int visconti_viif_isp_sub_set_unit(struct viif_device *viif_dev); +void visconti_viif_isp_set_compose_rect(struct viif_device *viif_dev, + struct viif_l2_roi_config *roi); + +/* viif_controls.c */ +int visconti_viif_isp_init_controls(struct viif_device *viif_dev); + +#endif /* VIIF_H */ diff --git a/drivers/media/platform/visconti/viif_capture.c b/drivers/media/platform/visconti/viif_capture.c new file mode 100644 index 00000000000..fa18aec4470 --- /dev/null +++ b/drivers/media/platform/visconti/viif_capture.c @@ -0,0 +1,1201 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2022 TOSHIBA CORPORATION + * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include +#include + +#include "viif.h" + +#define VIIF_CROP_MAX_X_ISP (8062U) +#define VIIF_CROP_MAX_Y_ISP (3966U) +#define VIIF_CROP_MIN_W (128U) +#define VIIF_CROP_MAX_W_ISP (8190U) +#define VIIF_CROP_MIN_H (128U) +#define VIIF_CROP_MAX_H_ISP (4094U) + +struct viif_buffer { + struct vb2_v4l2_buffer vb; + struct list_head queue; +}; + +static inline struct viif_buffer *vb2_to_viif(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct viif_buffer, vb); +} + +static inline struct cap_dev *video_drvdata_to_capdev(struct file *file) +{ + return (struct cap_dev *)video_drvdata(file); +} + +static inline struct cap_dev *vb2queue_to_capdev(struct vb2_queue *vq) +{ + return (struct cap_dev *)vb2_get_drv_priv(vq); +} + +/* ----- ISRs and VB2 Operations ----- */ +static int viif_set_img(struct cap_dev *cap_dev, struct vb2_buffer *vb) +{ + struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix; + struct viif_device *viif_dev = cap_dev->viif_dev; + struct hwd_viif_img next_out_img; + dma_addr_t phys_addr; + int i, ret = 0; + + next_out_img.width = pix->width; + next_out_img.height = pix->height; + next_out_img.format = cap_dev->out_format; + + for (i = 0; i < pix->num_planes; i++) { + next_out_img.pixelmap[i].pitch = pix->plane_fmt[i].bytesperline; + phys_addr = vb2_dma_contig_plane_dma_addr(vb, i); + next_out_img.pixelmap[i].pmap_paddr = phys_addr; + } + + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) { + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l2_set_img_transmission(viif_dev->hwd_res, VIIF_L2ISP_POST_0, + HWD_VIIF_ENABLE, &cap_dev->img_area, + &cap_dev->out_process, &next_out_img); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + if (ret) + dev_err(viif_dev->dev, "set img error. %d\n", ret); + } else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1) { + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l2_set_img_transmission(viif_dev->hwd_res, VIIF_L2ISP_POST_1, + HWD_VIIF_ENABLE, &cap_dev->img_area, + &cap_dev->out_process, &next_out_img); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + if (ret) + dev_err(viif_dev->dev, "set img error. %d\n", ret); + } else if (cap_dev->pathid == CAPTURE_PATH_SUB) { + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_sub_set_img_transmission(viif_dev->hwd_res, &next_out_img); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + if (ret) + dev_err(viif_dev->dev, "set img error. %d\n", ret); + } + + return ret; +} + +/* + * viif_capture_switch_buffer() is called from interrupt service routine + * triggered by VSync with some fixed delay. + * The function may switch DMA target buffer by calling viif_set_img(). + * The VIIF DMA HW captures the destination address at next VSync + * and completes transfer at one more after. + * Therefore, filled buffer is available at the one after next ISR. + * + * To avoid DMA HW getting stucked, we always need to set valid destination address. + * If a prepared buffer is not available, we reuse the buffer currently being transferred to. + * + * The cap_dev structure has two pointers and a queue to handle video buffers; + + Description of each item at the entry of this function: + * * buf_queue: holds prepared buffers, set by vb2_queue() + * * active: pointing at address captured (and to be filled) by DMA HW + * * dma_active: pointing at buffer filled by DMA HW + * + * Rules to update items: + * * when buf_queue is not empty, "active" buffer goes "dma_active" + * * when buf_queue is empty: + * * "active" buffer stays the same (DMA HW fills the same buffer for coming two frames) + * * "dma_active" gets NULL (filled buffer will be reused; should not go "DONE" at next ISR) + * + * Simulation: + * | buf_queue | active | dma_active | note | + * | X | NULL | NULL | | + * + * | X | BUF0 | NULL | BUF0 stays | + * | X | BUF0 | NULL | BUF0 stays | + * + * + * | BUF2 BUF1 | BUF0 | NULL | | + * | BUF2 | BUF1 | BUF0 | BUF0 goes DONE | + * | X | BUF2 | BUF1 | BUF1 goes DONE, BUF2 stays | + * | X | BUF2 | NULL | BUF2 stays | + */ +void visconti_viif_capture_switch_buffer(struct cap_dev *cap_dev, u32 status_err, + u32 l2_transfer_status, u64 timestamp) +{ + if (cap_dev->dma_active) { + /* DMA has completed and another framebuffer instance is set */ + struct vb2_v4l2_buffer *vbuf = cap_dev->dma_active; + enum vb2_buffer_state state; + + cap_dev->buf_cnt--; + vbuf->vb2_buf.timestamp = timestamp; + vbuf->sequence = cap_dev->sequence++; + vbuf->field = cap_dev->field; + if (status_err || l2_transfer_status) + state = VB2_BUF_STATE_ERROR; + else + state = VB2_BUF_STATE_DONE; + + vb2_buffer_done(&vbuf->vb2_buf, state); + } + + /* QUEUE pop to register an instance as next DMA target; if empty, reuse current instance */ + if (!list_empty(&cap_dev->buf_queue)) { + struct viif_buffer *buf = + list_entry(cap_dev->buf_queue.next, struct viif_buffer, queue); + list_del_init(&buf->queue); + viif_set_img(cap_dev, &buf->vb.vb2_buf); + cap_dev->active = &buf->vb; + cap_dev->dma_active = cap_dev->active; + } else { + cap_dev->dma_active = NULL; + } +} + +/* --- Capture buffer control --- */ +static int viif_vb2_setup(struct vb2_queue *vq, unsigned int *count, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct cap_dev *cap_dev = vb2queue_to_capdev(vq); + struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix; + unsigned int i; + + /* num_planes is set: just check plane sizes. */ + if (*num_planes) { + for (i = 0; i < pix->num_planes; i++) + if (sizes[i] < pix->plane_fmt[i].sizeimage) + return -EINVAL; + + return 0; + } + + /* num_planes not set: called from REQBUFS, just set plane sizes. */ + *num_planes = pix->num_planes; + for (i = 0; i < pix->num_planes; i++) + sizes[i] = pix->plane_fmt[i].sizeimage; + + cap_dev->buf_cnt = 0; + + return 0; +} + +static void viif_vb2_queue(struct vb2_buffer *vb) +{ + struct cap_dev *cap_dev = vb2queue_to_capdev(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct viif_device *viif_dev = cap_dev->viif_dev; + struct viif_buffer *buf = vb2_to_viif(vbuf); + unsigned long irqflags; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + + if (!cap_dev->active) { + cap_dev->active = vbuf; + viif_set_img(cap_dev, vb); + } else { + list_add_tail(&buf->queue, &cap_dev->buf_queue); + } + cap_dev->buf_cnt++; + + spin_unlock_irqrestore(&viif_dev->lock, irqflags); +} + +static int viif_vb2_prepare(struct vb2_buffer *vb) +{ + struct cap_dev *cap_dev = vb2queue_to_capdev(vb->vb2_queue); + struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix; + struct viif_device *viif_dev = cap_dev->viif_dev; + unsigned int i; + + for (i = 0; i < pix->num_planes; i++) { + if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) { + dev_err(viif_dev->dev, "Plane size too small (%lu < %u)\n", + vb2_plane_size(vb, i), pix->plane_fmt[i].sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage); + } + return 0; +} + +static int viif_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct cap_dev *cap_dev = vb2queue_to_capdev(vq); + struct viif_device *viif_dev = cap_dev->viif_dev; + struct viif_subdev *viif_sd = viif_dev->sd; + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + + /* note that pipe is shared among paths; see pipe.streaming_count member variable */ + ret = video_device_pipeline_start(&cap_dev->vdev, &viif_dev->pipe); + if (ret) + dev_err(viif_dev->dev, "start pipeline failed %d\n", ret); + + /* Currently, only path0 (MAIN POST0) initializes ISP and Camera */ + /* Possibly, initialization can be done when pipe.streaming_count==0 */ + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) { + /* CSI2RX start */ + ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, video, s_stream, true); + if (ret) { + dev_err(viif_dev->dev, "Start isp subdevice stream failed. %d\n", ret); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + return ret; + } + } + + /* buffer control */ + cap_dev->sequence = 0; + + /* finish critical section: some sensor driver (including imx219) calls schedule() */ + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + /* Camera (CSI2 source) start streaming */ + /* Currently, only path0 (MAIN POST0) initializes ISP and Camera */ + /* Possibly, initialization can be done when pipe.streaming_count==0 */ + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) { + ret = v4l2_subdev_call(viif_sd->v4l2_sd, video, s_stream, true); + if (ret) { + dev_err(viif_dev->dev, "Start subdev stream failed. %d\n", ret); + (void)v4l2_subdev_call(&viif_dev->isp_subdev.sd, video, s_stream, false); + return ret; + } + } + + return 0; +} + +static void viif_stop_streaming(struct vb2_queue *vq) +{ + struct cap_dev *cap_dev = vb2queue_to_capdev(vq); + struct viif_device *viif_dev = cap_dev->viif_dev; + struct viif_subdev *viif_sd = viif_dev->sd; + struct viif_buffer *buf; + unsigned long irqflags; + int ret; + + /* Currently, only path0 (MAIN POST0) stops ISP and Camera */ + /* Possibly, teardown can be done when pipe.streaming_count==0 */ + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) { + ret = v4l2_subdev_call(viif_sd->v4l2_sd, video, s_stream, false); + if (ret) + dev_err(viif_dev->dev, "Stop subdev stream failed. %d\n", ret); + } + + spin_lock_irqsave(&viif_dev->lock, irqflags); + + /* Currently, only path0 (MAIN POST0) stops ISP and Camera */ + /* Possibly, teardown can be done when pipe.streaming_count==0 */ + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) { + ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, video, s_stream, false); + if (ret) + dev_err(viif_dev->dev, "Stop isp subdevice stream failed %d\n", ret); + } + + /* buffer control */ + if (cap_dev->active) { + vb2_buffer_done(&cap_dev->active->vb2_buf, VB2_BUF_STATE_ERROR); + cap_dev->buf_cnt--; + cap_dev->active = NULL; + } + if (cap_dev->dma_active) { + vb2_buffer_done(&cap_dev->dma_active->vb2_buf, VB2_BUF_STATE_ERROR); + cap_dev->buf_cnt--; + cap_dev->dma_active = NULL; + } + + /* Release all queued buffers. */ + list_for_each_entry(buf, &cap_dev->buf_queue, queue) { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + cap_dev->buf_cnt--; + } + INIT_LIST_HEAD(&cap_dev->buf_queue); + if (cap_dev->buf_cnt) + dev_err(viif_dev->dev, "Buffer count error %d\n", cap_dev->buf_cnt); + + video_device_pipeline_stop(&cap_dev->vdev); + + spin_unlock_irqrestore(&viif_dev->lock, irqflags); +} + +static const struct vb2_ops viif_vb2_ops = { + .queue_setup = viif_vb2_setup, + .buf_queue = viif_vb2_queue, + .buf_prepare = viif_vb2_prepare, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = viif_start_streaming, + .stop_streaming = viif_stop_streaming, +}; + +/* --- VIIF hardware settings --- */ +/* L2ISP output csc setting for YUV to RGB(ITU-R BT.709) */ +static const struct hwd_viif_csc_param viif_csc_yuv2rgb = { + .r_cr_in_offset = 0x18000, + .g_y_in_offset = 0x1f000, + .b_cb_in_offset = 0x18000, + .coef = { + [0] = 0x1000, + [1] = 0xfd12, + [2] = 0xf8ad, + [3] = 0x1000, + [4] = 0x1d07, + [5] = 0x0000, + [6] = 0x1000, + [7] = 0x0000, + [8] = 0x18a2, + }, + .r_cr_out_offset = 0x1000, + .g_y_out_offset = 0x1000, + .b_cb_out_offset = 0x1000, +}; + +/* L2ISP output csc setting for RGB to YUV(ITU-R BT.709) */ +static const struct hwd_viif_csc_param viif_csc_rgb2yuv = { + .r_cr_in_offset = 0x1f000, + .g_y_in_offset = 0x1f000, + .b_cb_in_offset = 0x1f000, + .coef = { + [0] = 0x0b71, + [1] = 0x0128, + [2] = 0x0367, + [3] = 0xf9b1, + [4] = 0x082f, + [5] = 0xfe20, + [6] = 0xf891, + [7] = 0xff40, + [8] = 0x082f, + }, + .r_cr_out_offset = 0x8000, + .g_y_out_offset = 0x1000, + .b_cb_out_offset = 0x8000, +}; + +static int viif_l2_set_format(struct cap_dev *cap_dev) +{ + struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix; + struct viif_device *viif_dev = cap_dev->viif_dev; + const struct hwd_viif_csc_param *csc_param = NULL; + struct v4l2_subdev_selection sel = { + .target = V4L2_SEL_TGT_CROP, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + bool inp_is_rgb = false; + bool out_is_rgb = false; + u32 postid; + int ret; + + /* check path id */ + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) { + sel.pad = VIIF_ISP_PAD_SRC_PATH0; + fmt.pad = VIIF_ISP_PAD_SRC_PATH0; + postid = VIIF_L2ISP_POST_0; + } else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1) { + sel.pad = VIIF_ISP_PAD_SRC_PATH1; + fmt.pad = VIIF_ISP_PAD_SRC_PATH1; + postid = VIIF_L2ISP_POST_1; + } else { + return -EINVAL; + } + + cap_dev->out_process.half_scale = HWD_VIIF_DISABLE; + cap_dev->out_process.select_color = HWD_VIIF_COLOR_YUV_RGB; + cap_dev->out_process.alpha = 0; + + ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, pad, get_selection, NULL, &sel); + if (ret) { + cap_dev->img_area.x = 0; + cap_dev->img_area.y = 0; + cap_dev->img_area.w = pix->width; + cap_dev->img_area.h = pix->height; + } else { + cap_dev->img_area.x = sel.r.left; + cap_dev->img_area.y = sel.r.top; + cap_dev->img_area.w = sel.r.width; + cap_dev->img_area.h = sel.r.height; + } + + ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, pad, get_fmt, NULL, &fmt); + if (!ret) + inp_is_rgb = (fmt.format.code == MEDIA_BUS_FMT_RGB888_1X24); + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_RGB24: + cap_dev->out_format = HWD_VIIF_RGB888_PACKED; + out_is_rgb = true; + break; + case V4L2_PIX_FMT_ABGR32: + cap_dev->out_format = HWD_VIIF_ARGB8888_PACKED; + cap_dev->out_process.alpha = 0xff; + out_is_rgb = true; + break; + case V4L2_PIX_FMT_YUV422M: + cap_dev->out_format = HWD_VIIF_YCBCR422_8_PLANAR; + break; + case V4L2_PIX_FMT_YUV444M: + cap_dev->out_format = HWD_VIIF_RGB888_YCBCR444_8_PLANAR; + break; + case V4L2_PIX_FMT_Y16: + cap_dev->out_format = HWD_VIIF_ONE_COLOR_16; + cap_dev->out_process.select_color = HWD_VIIF_COLOR_Y_G; + break; + } + + if (!inp_is_rgb && out_is_rgb) + csc_param = &viif_csc_yuv2rgb; /* YUV -> RGB */ + else if (inp_is_rgb && !out_is_rgb) + csc_param = &viif_csc_rgb2yuv; /* RGB -> YUV */ + + return hwd_viif_l2_set_output_csc(viif_dev->hwd_res, postid, csc_param); +} + +/* --- IOCTL Operations --- */ +static const struct viif_fmt viif_fmt_list[] = { + { + .fourcc = V4L2_PIX_FMT_RGB24, + .bpp = { 24, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 384, + }, + { + .fourcc = V4L2_PIX_FMT_ABGR32, + .bpp = { 32, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 512, + }, + { + .fourcc = V4L2_PIX_FMT_YUV422M, + .bpp = { 8, 4, 4 }, + .num_planes = 3, + .colorspace = V4L2_COLORSPACE_REC709, + .pitch_align = 128, + }, + { + .fourcc = V4L2_PIX_FMT_YUV444M, + .bpp = { 8, 8, 8 }, + .num_planes = 3, + .colorspace = V4L2_COLORSPACE_REC709, + .pitch_align = 128, + }, + { + .fourcc = V4L2_PIX_FMT_Y16, + .bpp = { 16, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_REC709, + .pitch_align = 128, + }, +}; + +static const struct viif_fmt viif_rawfmt_list[] = { + { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .bpp = { 16, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 256, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .bpp = { 16, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 256, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB14, + .bpp = { 16, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 256, + }, +}; + +static const struct viif_fmt *get_viif_fmt_from_fourcc(unsigned int fourcc) +{ + const struct viif_fmt *fmt = &viif_fmt_list[0]; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(viif_fmt_list); i++, fmt++) + if (fmt->fourcc == fourcc) + return fmt; + + return NULL; +} + +static void viif_update_plane_sizes(struct v4l2_plane_pix_format *plane, unsigned int bpl, + unsigned int szimage) +{ + memset(plane, 0, sizeof(*plane)); + + plane->sizeimage = szimage; + plane->bytesperline = bpl; +} + +static void viif_calc_plane_sizes(const struct viif_fmt *viif_fmt, + struct v4l2_pix_format_mplane *pix) +{ + unsigned int i, bpl, szimage; + + for (i = 0; i < viif_fmt->num_planes; i++) { + bpl = pix->width * viif_fmt->bpp[i] / 8; + /* round up ptch */ + bpl = (bpl + (viif_fmt->pitch_align - 1)) / viif_fmt->pitch_align; + bpl *= viif_fmt->pitch_align; + szimage = pix->height * bpl; + viif_update_plane_sizes(&pix->plane_fmt[i], bpl, szimage); + } + pix->num_planes = viif_fmt->num_planes; +} + +static int viif_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + + strscpy(cap->card, "Toshiba VIIF", sizeof(cap->card)); + strscpy(cap->driver, "viif", sizeof(cap->driver)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:toshiba-viif-%s", + dev_name(viif_dev->dev)); + return 0; +} + +static int viif_enum_rawfmt(struct cap_dev *cap_dev, struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(viif_rawfmt_list)) + return -EINVAL; + + f->pixelformat = viif_rawfmt_list[f->index].fourcc; + + return 0; +} + +static int viif_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + const struct viif_fmt *fmt; + + if (cap_dev->pathid == CAPTURE_PATH_SUB) + return viif_enum_rawfmt(cap_dev, f); + + if (f->index >= ARRAY_SIZE(viif_fmt_list)) + return -EINVAL; + + fmt = &viif_fmt_list[f->index]; + f->pixelformat = fmt->fourcc; + + return 0; +} + +/* size of minimum/maximum output image */ +#define VIIF_MIN_OUTPUT_IMG_WIDTH (128U) +#define VIIF_MAX_OUTPUT_IMG_WIDTH_ISP (5760U) +#define VIIF_MAX_OUTPUT_IMG_WIDTH_SUB (4096U) + +#define VIIF_MIN_OUTPUT_IMG_HEIGHT (128U) +#define VIIF_MAX_OUTPUT_IMG_HEIGHT_ISP (3240U) +#define VIIF_MAX_OUTPUT_IMG_HEIGHT_SUB (2160U) + +static int viif_try_fmt(struct cap_dev *cap_dev, struct v4l2_format *v4l2_fmt) +{ + struct v4l2_pix_format_mplane *pix = &v4l2_fmt->fmt.pix_mp; + struct viif_device *viif_dev = cap_dev->viif_dev; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + const struct viif_fmt *viif_fmt; + int ret; + + /* check path id */ + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) + format.pad = VIIF_ISP_PAD_SRC_PATH0; + else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1) + format.pad = VIIF_ISP_PAD_SRC_PATH1; + else + format.pad = VIIF_ISP_PAD_SRC_PATH2; + + ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, pad, get_fmt, NULL, &format); + if (ret) + return -EINVAL; + + /* fourcc check */ + if (cap_dev->pathid == CAPTURE_PATH_SUB) { + switch (format.format.code) { + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + viif_fmt = &viif_rawfmt_list[0]; /*V4L2_PIX_FMT_SRGGB10*/ + pix->pixelformat = viif_fmt->fourcc; + break; + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + viif_fmt = &viif_rawfmt_list[1]; /*V4L2_PIX_FMT_SRGGB12*/ + pix->pixelformat = viif_fmt->fourcc; + break; + case MEDIA_BUS_FMT_SRGGB14_1X14: + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SBGGR14_1X14: + viif_fmt = &viif_rawfmt_list[2]; /*V4L2_PIX_FMT_SRGGB14*/ + pix->pixelformat = viif_fmt->fourcc; + break; + default: + return -EINVAL; + } + } else { + viif_fmt = get_viif_fmt_from_fourcc(pix->pixelformat); + if (!viif_fmt) + return -EINVAL; + } + + /* min/max width, height check */ + if (pix->width < VIIF_MIN_OUTPUT_IMG_WIDTH) + pix->width = VIIF_MIN_OUTPUT_IMG_WIDTH; + + if (pix->width > VIIF_MAX_OUTPUT_IMG_WIDTH_ISP) + pix->width = VIIF_MAX_OUTPUT_IMG_WIDTH_ISP; + + if (pix->height < VIIF_MIN_OUTPUT_IMG_HEIGHT) + pix->height = VIIF_MIN_OUTPUT_IMG_HEIGHT; + + if (pix->height > VIIF_MAX_OUTPUT_IMG_HEIGHT_ISP) + pix->height = VIIF_MAX_OUTPUT_IMG_HEIGHT_ISP; + + /* consistency with isp::pad::src::fmt */ + if (pix->width != format.format.width) + return -EINVAL; + if (pix->height != format.format.height) + return -EINVAL; + + /* update derived parameters, such as bpp */ + viif_calc_plane_sizes(viif_fmt, pix); + + return 0; +} + +static int viif_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + + return viif_try_fmt(cap_dev, f); +} + +static int viif_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + struct viif_device *viif_dev = cap_dev->viif_dev; + int ret = 0; + + if (vb2_is_streaming(&cap_dev->vb2_vq)) + return -EBUSY; + + if (f->type != cap_dev->vb2_vq.type) + return -EINVAL; + + ret = viif_try_fmt(cap_dev, f); + if (ret) + return ret; + + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) { + /* + * A call to main_set_unit() is currently at ioctl(VIDIOC_S_FMT) context. + * This call can be moved to viif_isp_s_stream(), + * if you don't want to check the given format is compatible to HW. + */ + ret = visconti_viif_isp_main_set_unit(viif_dev); + if (ret) + return ret; + } + + cap_dev->v4l2_pix = f->fmt.pix_mp; + cap_dev->field = V4L2_FIELD_NONE; + + if (cap_dev->pathid == CAPTURE_PATH_SUB) { + cap_dev->out_format = HWD_VIIF_ONE_COLOR_16; + ret = visconti_viif_isp_sub_set_unit(viif_dev); + } else { + ret = viif_l2_set_format(cap_dev); + } + + return ret; +} + +static int viif_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + + f->fmt.pix_mp = cap_dev->v4l2_pix; + + return 0; +} + +static int viif_enum_input(struct file *file, void *priv, struct v4l2_input *inp) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + struct viif_subdev *viif_sd; + struct v4l2_subdev *v4l2_sd; + int ret; + + if (inp->index >= viif_dev->num_sd) + return -EINVAL; + + viif_sd = &viif_dev->subdevs[inp->index]; + v4l2_sd = viif_sd->v4l2_sd; + + ret = v4l2_subdev_call(v4l2_sd, video, g_input_status, &inp->status); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = 0; + if (v4l2_subdev_has_op(v4l2_sd, pad, dv_timings_cap)) + inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; + else + inp->capabilities = V4L2_IN_CAP_STD; + snprintf(inp->name, sizeof(inp->name), "Camera%u: %s", inp->index, viif_sd->v4l2_sd->name); + + return 0; +} + +static int viif_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + + *i = viif_dev->sd_index; + + return 0; +} + +static int viif_s_input(struct file *file, void *priv, unsigned int i) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + + if (i >= viif_dev->num_sd) + return -EINVAL; + + return 0; +} + +static int viif_g_selection(struct file *file, void *priv, struct v4l2_selection *s) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + struct viif_device *viif_dev = cap_dev->viif_dev; + struct v4l2_subdev_selection sel = { + .target = V4L2_SEL_TGT_CROP, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + /* check path id */ + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) + sel.pad = VIIF_ISP_PAD_SRC_PATH0; + else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1) + sel.pad = VIIF_ISP_PAD_SRC_PATH1; + else + return -EINVAL; + + ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, pad, get_selection, NULL, &sel); + s->r = sel.r; + + return ret; +} + +static int viif_s_selection(struct file *file, void *priv, struct v4l2_selection *s) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + struct viif_device *viif_dev = cap_dev->viif_dev; + struct v4l2_subdev_selection sel = { + .target = V4L2_SEL_TGT_CROP, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .r = s->r, + }; + int ret; + + /* check path id */ + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) + sel.pad = VIIF_ISP_PAD_SRC_PATH0; + else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1) + sel.pad = VIIF_ISP_PAD_SRC_PATH1; + else + return -EINVAL; + + if (s->r.left > VIIF_CROP_MAX_X_ISP || s->r.top > VIIF_CROP_MAX_Y_ISP || + s->r.width < VIIF_CROP_MIN_W || s->r.width > VIIF_CROP_MAX_W_ISP || + s->r.height < VIIF_CROP_MIN_H || s->r.height > VIIF_CROP_MAX_H_ISP) { + return -EINVAL; + } + + ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, pad, set_selection, NULL, &sel); + s->r = sel.r; + + return ret; +} + +static int viif_dv_timings_cap(struct file *file, void *priv_fh, struct v4l2_dv_timings_cap *cap) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + struct viif_subdev *viif_sd = viif_dev->sd; + + return v4l2_subdev_call(viif_sd->v4l2_sd, pad, dv_timings_cap, cap); +} + +static int viif_enum_dv_timings(struct file *file, void *priv_fh, + struct v4l2_enum_dv_timings *timings) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + struct viif_subdev *viif_sd = viif_dev->sd; + + return v4l2_subdev_call(viif_sd->v4l2_sd, pad, enum_dv_timings, timings); +} + +static int viif_g_dv_timings(struct file *file, void *priv_fh, struct v4l2_dv_timings *timings) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + struct viif_subdev *viif_sd = viif_dev->sd; + + return v4l2_subdev_call(viif_sd->v4l2_sd, video, g_dv_timings, timings); +} + +static int viif_s_dv_timings(struct file *file, void *priv_fh, struct v4l2_dv_timings *timings) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + struct viif_subdev *viif_sd = viif_dev->sd; + + return v4l2_subdev_call(viif_sd->v4l2_sd, video, s_dv_timings, timings); +} + +static int viif_query_dv_timings(struct file *file, void *priv_fh, struct v4l2_dv_timings *timings) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + struct viif_subdev *viif_sd = viif_dev->sd; + + return v4l2_subdev_call(viif_sd->v4l2_sd, video, query_dv_timings, timings); +} + +static int viif_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + struct viif_subdev *viif_sd = viif_dev->sd; + + return v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_edid, edid); +} + +static int viif_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + struct viif_subdev *viif_sd = viif_dev->sd; + + return v4l2_subdev_call(viif_sd->v4l2_sd, pad, set_edid, edid); +} + +static int viif_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + + return v4l2_g_parm_cap(video_devdata(file), viif_dev->sd->v4l2_sd, a); +} + +static int viif_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + + return v4l2_s_parm_cap(video_devdata(file), viif_dev->sd->v4l2_sd, a); +} + +static int viif_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + struct viif_subdev *viif_sd = viif_dev->sd; + struct v4l2_subdev *v4l2_sd = viif_sd->v4l2_sd; + struct v4l2_subdev_frame_size_enum fse = { + .code = viif_sd->mbus_code, + .index = fsize->index, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_size, NULL, &fse); + if (ret) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + return 0; +} + +static int viif_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival) +{ + struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev; + struct viif_subdev *viif_sd = viif_dev->sd; + struct v4l2_subdev *v4l2_sd = viif_sd->v4l2_sd; + struct v4l2_subdev_frame_interval_enum fie = { + .code = viif_sd->mbus_code, + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_interval, NULL, &fie); + if (ret) + return ret; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static const struct v4l2_ioctl_ops viif_ioctl_ops = { + .vidioc_querycap = viif_querycap, + + .vidioc_enum_fmt_vid_cap = viif_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap_mplane = viif_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap_mplane = viif_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane = viif_g_fmt_vid_cap, + + .vidioc_enum_input = viif_enum_input, + .vidioc_g_input = viif_g_input, + .vidioc_s_input = viif_s_input, + + .vidioc_g_selection = viif_g_selection, + .vidioc_s_selection = viif_s_selection, + + .vidioc_dv_timings_cap = viif_dv_timings_cap, + .vidioc_enum_dv_timings = viif_enum_dv_timings, + .vidioc_g_dv_timings = viif_g_dv_timings, + .vidioc_s_dv_timings = viif_s_dv_timings, + .vidioc_query_dv_timings = viif_query_dv_timings, + + .vidioc_g_edid = viif_g_edid, + .vidioc_s_edid = viif_s_edid, + + .vidioc_g_parm = viif_g_parm, + .vidioc_s_parm = viif_s_parm, + + .vidioc_enum_framesizes = viif_enum_framesizes, + .vidioc_enum_frameintervals = viif_enum_frameintervals, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* --- File Operations --- */ +static int viif_capture_open(struct file *file) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + struct viif_device *viif_dev = cap_dev->viif_dev; + int ret; + + ret = v4l2_fh_open(file); + if (ret) + return ret; + + return pm_runtime_resume_and_get(viif_dev->dev); +} + +static int viif_capture_release(struct file *file) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + struct viif_device *viif_dev = cap_dev->viif_dev; + + vb2_fop_release(file); + pm_runtime_put(viif_dev->dev); + + return 0; +} + +static const struct v4l2_file_operations viif_fops = { + .owner = THIS_MODULE, + .open = viif_capture_open, + .release = viif_capture_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +/* ----- media control callbacks ----- */ +static int viif_capture_link_validate(struct media_link *link) +{ + /* link validation at start-stream */ + return 0; +} + +static const struct media_entity_operations viif_media_ops = { + .link_validate = viif_capture_link_validate, +}; + +/* ----- attach ctrl callbacck handler ----- */ +int visconti_viif_capture_register_ctrl_handlers(struct viif_device *viif_dev) +{ + int ret; + + /* MAIN POST0: merge controls of ISP and CAPTURE0 */ + ret = v4l2_ctrl_add_handler(&viif_dev->cap_dev0.ctrl_handler, + viif_dev->sd->v4l2_sd->ctrl_handler, NULL, true); + if (ret) { + dev_err(viif_dev->dev, "Failed to add sensor ctrl_handler"); + return ret; + } + ret = v4l2_ctrl_add_handler(&viif_dev->cap_dev0.ctrl_handler, + &viif_dev->isp_subdev.ctrl_handler, NULL, true); + if (ret) { + dev_err(viif_dev->dev, "Failed to add isp subdev ctrl_handler"); + return ret; + } + + /* MAIN POST1: merge controls of ISP and CAPTURE0 */ + ret = v4l2_ctrl_add_handler(&viif_dev->cap_dev1.ctrl_handler, + viif_dev->sd->v4l2_sd->ctrl_handler, NULL, true); + if (ret) { + dev_err(viif_dev->dev, "Failed to add sensor ctrl_handler"); + return ret; + } + ret = v4l2_ctrl_add_handler(&viif_dev->cap_dev1.ctrl_handler, + &viif_dev->isp_subdev.ctrl_handler, NULL, true); + if (ret) { + dev_err(viif_dev->dev, "Failed to add isp subdev ctrl_handler"); + return ret; + } + + /* SUB: no control is exported */ + + return 0; +} + +/* ----- register/remove capture device node ----- */ +static int visconti_viif_capture_register_node(struct cap_dev *cap_dev) +{ + struct viif_device *viif_dev = cap_dev->viif_dev; + struct v4l2_device *v4l2_dev = &viif_dev->v4l2_dev; + struct video_device *vdev = &cap_dev->vdev; + struct vb2_queue *q = &cap_dev->vb2_vq; + static const char *const node_name[] = { + "viif_capture_post0", + "viif_capture_post1", + "viif_capture_sub", + }; + int ret; + + INIT_LIST_HEAD(&cap_dev->buf_queue); + + mutex_init(&cap_dev->vlock); + + /* Initialize vb2 queue. */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->io_modes = VB2_DMABUF; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->ops = &viif_vb2_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->drv_priv = cap_dev; + q->buf_struct_size = sizeof(struct viif_buffer); + q->min_buffers_needed = 2; + q->lock = &cap_dev->vlock; + q->dev = viif_dev->v4l2_dev.dev; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + /* Register the video device. */ + strscpy(vdev->name, node_name[cap_dev->pathid], sizeof(vdev->name)); + vdev->v4l2_dev = v4l2_dev; + vdev->lock = &cap_dev->vlock; + vdev->queue = &cap_dev->vb2_vq; + vdev->ctrl_handler = NULL; + vdev->fops = &viif_fops; + vdev->ioctl_ops = &viif_ioctl_ops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; + vdev->device_caps |= V4L2_CAP_IO_MC; + vdev->entity.ops = &viif_media_ops; + vdev->release = video_device_release_empty; + video_set_drvdata(vdev, cap_dev); + vdev->vfl_dir = VFL_DIR_RX; + cap_dev->capture_pad.flags = MEDIA_PAD_FL_SINK; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + dev_err(v4l2_dev->dev, "video_register_device failed: %d\n", ret); + return ret; + } + + ret = media_entity_pads_init(&vdev->entity, 1, &cap_dev->capture_pad); + if (ret) { + video_unregister_device(vdev); + return ret; + } + + ret = v4l2_ctrl_handler_init(&cap_dev->ctrl_handler, 30); + if (ret) + return -ENOMEM; + + cap_dev->vdev.ctrl_handler = &cap_dev->ctrl_handler; + + return 0; +} + +int visconti_viif_capture_register(struct viif_device *viif_dev) +{ + int ret; + + /* register MAIN POST0 (primary RGB output)*/ + viif_dev->cap_dev0.pathid = CAPTURE_PATH_MAIN_POST0; + viif_dev->cap_dev0.viif_dev = viif_dev; + ret = visconti_viif_capture_register_node(&viif_dev->cap_dev0); + if (ret) + return ret; + + /* register MAIN POST1 (additional RGB output)*/ + viif_dev->cap_dev1.pathid = CAPTURE_PATH_MAIN_POST1; + viif_dev->cap_dev1.viif_dev = viif_dev; + ret = visconti_viif_capture_register_node(&viif_dev->cap_dev1); + if (ret) + return ret; + + /* register SUB (RAW output) */ + viif_dev->cap_dev2.pathid = CAPTURE_PATH_SUB; + viif_dev->cap_dev2.viif_dev = viif_dev; + ret = visconti_viif_capture_register_node(&viif_dev->cap_dev2); + if (ret) + return ret; + + return 0; +} + +static void visconti_viif_capture_unregister_node(struct cap_dev *cap_dev) +{ + media_entity_cleanup(&cap_dev->vdev.entity); + v4l2_ctrl_handler_free(&cap_dev->ctrl_handler); + vb2_video_unregister_device(&cap_dev->vdev); + mutex_destroy(&cap_dev->vlock); +} + +void visconti_viif_capture_unregister(struct viif_device *viif_dev) +{ + visconti_viif_capture_unregister_node(&viif_dev->cap_dev0); + visconti_viif_capture_unregister_node(&viif_dev->cap_dev1); + visconti_viif_capture_unregister_node(&viif_dev->cap_dev2); +} diff --git a/drivers/media/platform/visconti/viif_isp.c b/drivers/media/platform/visconti/viif_isp.c new file mode 100644 index 00000000000..9314e6e8661 --- /dev/null +++ b/drivers/media/platform/visconti/viif_isp.c @@ -0,0 +1,846 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2022 TOSHIBA CORPORATION + * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include + +#include "viif.h" + +/* ----- supported MBUS formats ----- */ +struct visconti_mbus_format { + unsigned int code; + unsigned int bpp; + int rgb_out; +} static visconti_mbus_formats[] = { + { .code = MEDIA_BUS_FMT_RGB888_1X24, .bpp = 24, .rgb_out = 1 }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_UYVY10_1X20, .bpp = 20, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_RGB565_1X16, .bpp = 16, .rgb_out = 1 }, + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SRGGB14_1X14, .bpp = 14, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SGRBG14_1X14, .bpp = 14, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SGBRG14_1X14, .bpp = 14, .rgb_out = 0 }, + { .code = MEDIA_BUS_FMT_SBGGR14_1X14, .bpp = 14, .rgb_out = 0 }, +}; + +static int viif_get_mbus_rgb_out(unsigned int mbus_code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(visconti_mbus_formats); i++) + if (visconti_mbus_formats[i].code == mbus_code) + return visconti_mbus_formats[i].rgb_out; + + /* YUV intermediate code by default */ + return 0; +} + +static unsigned int viif_get_mbus_bpp(unsigned int mbus_code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(visconti_mbus_formats); i++) + if (visconti_mbus_formats[i].code == mbus_code) + return visconti_mbus_formats[i].bpp; + + /* default bpp value */ + return 24; +} + +static bool viif_is_valid_mbus_code(unsigned int mbus_code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(visconti_mbus_formats); i++) + if (visconti_mbus_formats[i].code == mbus_code) + return true; + return false; +} + +/* ----- handling main processing path ----- */ +static int viif_get_dv_timings(struct viif_device *viif_dev, struct v4l2_dv_timings *timings) +{ + struct viif_subdev *viif_sd = viif_dev->sd; + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_state pad_state = { + .pads = &pad_cfg, + }; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = 0, + }; + struct v4l2_ctrl *ctrl; + int ret; + + /* some video I/F support dv_timings query */ + ret = v4l2_subdev_call(viif_sd->v4l2_sd, video, g_dv_timings, timings); + if (ret == 0) + return 0; + + /* others: call some discrete APIs */ + ret = v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_fmt, &pad_state, &format); + if (ret != 0) + return ret; + + timings->bt.width = format.format.width; + timings->bt.height = format.format.height; + + ctrl = v4l2_ctrl_find(viif_sd->v4l2_sd->ctrl_handler, V4L2_CID_HBLANK); + if (!ctrl) { + dev_err(viif_dev->dev, "subdev: V4L2_CID_VBLANK error.\n"); + return -EINVAL; + } + timings->bt.hsync = v4l2_ctrl_g_ctrl(ctrl); + + ctrl = v4l2_ctrl_find(viif_sd->v4l2_sd->ctrl_handler, V4L2_CID_VBLANK); + if (!ctrl) { + dev_err(viif_dev->dev, "subdev: V4L2_CID_VBLANK error.\n"); + return -EINVAL; + } + timings->bt.vsync = v4l2_ctrl_g_ctrl(ctrl); + + ctrl = v4l2_ctrl_find(viif_sd->v4l2_sd->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + dev_err(viif_dev->dev, "subdev: V4L2_CID_PIXEL_RATE error.\n"); + return -EINVAL; + } + timings->bt.pixelclock = v4l2_ctrl_g_ctrl_int64(ctrl); + + return 0; +} + +int visconti_viif_isp_main_set_unit(struct viif_device *viif_dev) +{ + unsigned int dt_image, color_type, rawpack, yuv_conv; + struct viif_subdev *viif_sd = viif_dev->sd; + struct hwd_viif_input_img in_img_main; + struct viif_l2_undist undist = { 0 }; + struct v4l2_dv_timings timings; + struct v4l2_subdev_format fmt = { + .pad = 0, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int mag_hactive = 1; + int ret = 0; + + ret = viif_get_dv_timings(viif_dev, &timings); + if (ret) { + dev_err(viif_dev->dev, "could not get timing information of subdev"); + return -EINVAL; + } + + ret = v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_fmt, NULL, &fmt); + if (ret) { + dev_err(viif_dev->dev, "could not get pad information of subdev"); + return -EINVAL; + } + + switch (fmt.format.code) { + case MEDIA_BUS_FMT_RGB888_1X24: + dt_image = VISCONTI_CSI2_DT_RGB888; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + dt_image = VISCONTI_CSI2_DT_YUV4228B; + break; + case MEDIA_BUS_FMT_UYVY10_1X20: + dt_image = VISCONTI_CSI2_DT_YUV42210B; + break; + case MEDIA_BUS_FMT_RGB565_1X16: + dt_image = VISCONTI_CSI2_DT_RGB565; + break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + dt_image = VISCONTI_CSI2_DT_RAW8; + break; + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + dt_image = VISCONTI_CSI2_DT_RAW10; + break; + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + dt_image = VISCONTI_CSI2_DT_RAW12; + break; + case MEDIA_BUS_FMT_SRGGB14_1X14: + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SBGGR14_1X14: + dt_image = VISCONTI_CSI2_DT_RAW14; + break; + default: + dt_image = VISCONTI_CSI2_DT_RGB888; + break; + } + + color_type = dt_image; + + if (color_type == VISCONTI_CSI2_DT_RAW8 || color_type == VISCONTI_CSI2_DT_RAW10 || + color_type == VISCONTI_CSI2_DT_RAW12) { + rawpack = viif_dev->rawpack_mode; + if (rawpack != HWD_VIIF_RAWPACK_DISABLE) + mag_hactive = 2; + } else { + rawpack = HWD_VIIF_RAWPACK_DISABLE; + } + + if (color_type == VISCONTI_CSI2_DT_YUV4228B || color_type == VISCONTI_CSI2_DT_YUV42210B) + yuv_conv = HWD_VIIF_YUV_CONV_INTERPOLATION; + else + yuv_conv = HWD_VIIF_YUV_CONV_REPEAT; + + in_img_main.hactive_size = timings.bt.width; + in_img_main.vactive_size = timings.bt.height; + in_img_main.htotal_size = timings.bt.width * mag_hactive + timings.bt.hsync; + in_img_main.vtotal_size = timings.bt.height + timings.bt.vsync; + in_img_main.pixel_clock = timings.bt.pixelclock / 1000; + in_img_main.vbp_size = timings.bt.vsync - 5; + + in_img_main.interpolation_mode = HWD_VIIF_L1_INPUT_INTERPOLATION_LINE; + in_img_main.input_num = 1; + in_img_main.hobc_width = 0; + in_img_main.hobc_margin = 0; + + /* configuration of MAIN unit */ + ret = hwd_viif_main_set_unit(viif_dev->hwd_res, dt_image, &in_img_main, color_type, rawpack, + yuv_conv); + if (ret) { + dev_err(viif_dev->dev, "main_set_unit error. %d\n", ret); + return ret; + } + + /* Enable regbuf */ + hwd_viif_isp_set_regbuf_auto_transmission(viif_dev->hwd_res); + + /* L2 UNDIST Enable through mode as default */ + undist.through_mode = HWD_VIIF_ENABLE; + undist.sensor_crop_ofs_h = 1 - in_img_main.hactive_size; + undist.sensor_crop_ofs_v = 1 - in_img_main.vactive_size; + undist.grid_node_num_h = 16; + undist.grid_node_num_v = 16; + ret = hwd_viif_l2_set_undist(viif_dev->hwd_res, &undist); + if (ret) + dev_err(viif_dev->dev, "l2_set_undist error. %d\n", ret); + return ret; +} + +static unsigned int dt_image_from_mbus_code(unsigned int mbus_code) +{ + switch (mbus_code) { + case MEDIA_BUS_FMT_RGB888_1X24: + return VISCONTI_CSI2_DT_RGB888; + case MEDIA_BUS_FMT_UYVY8_1X16: + return VISCONTI_CSI2_DT_YUV4228B; + case MEDIA_BUS_FMT_UYVY10_1X20: + return VISCONTI_CSI2_DT_YUV42210B; + case MEDIA_BUS_FMT_RGB565_1X16: + return VISCONTI_CSI2_DT_RGB565; + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + return VISCONTI_CSI2_DT_RAW8; + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + return VISCONTI_CSI2_DT_RAW10; + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + return VISCONTI_CSI2_DT_RAW12; + case MEDIA_BUS_FMT_SRGGB14_1X14: + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SBGGR14_1X14: + return VISCONTI_CSI2_DT_RAW14; + default: + return VISCONTI_CSI2_DT_RGB888; + } +} + +int visconti_viif_isp_sub_set_unit(struct viif_device *viif_dev) +{ + struct hwd_viif_input_img in_img_sub; + struct v4l2_dv_timings timings; + struct v4l2_subdev_format fmt = { + .pad = 0, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + unsigned int dt_image; + int ret; + + ret = viif_get_dv_timings(viif_dev, &timings); + if (ret) + return -EINVAL; + + ret = v4l2_subdev_call(viif_dev->sd->v4l2_sd, pad, get_fmt, NULL, &fmt); + if (ret) { + dev_err(viif_dev->dev, "could not get pad information of subdev"); + return -EINVAL; + } + + dt_image = dt_image_from_mbus_code(fmt.format.code); + + in_img_sub.hactive_size = 0; + in_img_sub.vactive_size = timings.bt.height; + in_img_sub.htotal_size = timings.bt.width + timings.bt.hsync; + in_img_sub.vtotal_size = timings.bt.height + timings.bt.vsync; + in_img_sub.pixel_clock = timings.bt.pixelclock / 1000; + in_img_sub.vbp_size = timings.bt.vsync - 5; + in_img_sub.interpolation_mode = HWD_VIIF_L1_INPUT_INTERPOLATION_LINE; + in_img_sub.input_num = 1; + in_img_sub.hobc_width = 0; + in_img_sub.hobc_margin = 0; + + ret = hwd_viif_sub_set_unit(viif_dev->hwd_res, dt_image, &in_img_sub); + if (ret) + dev_err(viif_dev->dev, "sub_set_unit error. %d\n", ret); + + return ret; +}; + +/* ----- handling CSI2RX hardware ----- */ +static int viif_csi2rx_initialize(struct viif_device *viif_dev) +{ + struct hwd_viif_csi2rx_line_err_target err_target = { 0 }; + struct hwd_viif_csi2rx_irq_mask csi2rx_mask; + struct viif_subdev *viif_sd = viif_dev->sd; + struct v4l2_mbus_config cfg = { 0 }; + struct v4l2_subdev_format fmt = { + .pad = 0, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_dv_timings timings; + int num_lane, dphy_rate; + int ret; + + ret = v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_mbus_config, 0, &cfg); + if (ret) { + dev_dbg(viif_dev->dev, "subdev: g_mbus_config error. %d\n", ret); + num_lane = viif_sd->num_lane; + } else { + if (cfg.type != V4L2_MBUS_CSI2_DPHY) + return -EINVAL; + num_lane = cfg.bus.mipi_csi2.num_data_lanes; + } + + ret = v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_fmt, 0, &fmt); + if (ret) + return -EINVAL; + + ret = viif_get_dv_timings(viif_dev, &timings); + if (ret) + return -EINVAL; + + dphy_rate = (timings.bt.pixelclock / 1000) * viif_get_mbus_bpp(fmt.format.code) / num_lane; + dphy_rate = dphy_rate / 1000; + + /* check error for CH0: all supported DTs */ + err_target.dt[0] = VISCONTI_CSI2_DT_RGB565; + err_target.dt[1] = VISCONTI_CSI2_DT_YUV4228B; + err_target.dt[2] = VISCONTI_CSI2_DT_YUV42210B; + err_target.dt[3] = VISCONTI_CSI2_DT_RGB888; + err_target.dt[4] = VISCONTI_CSI2_DT_RAW8; + err_target.dt[5] = VISCONTI_CSI2_DT_RAW10; + err_target.dt[6] = VISCONTI_CSI2_DT_RAW12; + err_target.dt[7] = VISCONTI_CSI2_DT_RAW14; + + /* Define errors to be masked */ + csi2rx_mask.mask[0] = 0x0000000F; /*check all for PHY_FATAL*/ + csi2rx_mask.mask[1] = 0x0001000F; /*check all for PKT_FATAL*/ + csi2rx_mask.mask[2] = 0x000F0F0F; /*check all for FRAME_FATAL*/ + csi2rx_mask.mask[3] = 0x000F000F; /*check all for PHY*/ + csi2rx_mask.mask[4] = 0x000F000F; /*check all for PKT*/ + csi2rx_mask.mask[5] = 0x00FF00FF; /*check all for LINE*/ + + return hwd_viif_csi2rx_initialize(viif_dev->hwd_res, num_lane, HWD_VIIF_CSI2_DPHY_L0L1L2L3, + dphy_rate, HWD_VIIF_ENABLE, &err_target, &csi2rx_mask); +} + +static int viif_csi2rx_start(struct viif_device *viif_dev) +{ + struct hwd_viif_csi2rx_packet packet = { 0 }; + u32 vc_main = 0; + u32 vc_sub = 0; + + viif_dev->masked_gamma_path = 0U; + + return hwd_viif_csi2rx_start(viif_dev->hwd_res, vc_main, vc_sub, &packet); +} + +static int viif_csi2rx_stop(struct viif_device *viif_dev) +{ + s32 ret; + + ret = hwd_viif_csi2rx_stop(viif_dev->hwd_res); + if (ret) + dev_err(viif_dev->dev, "csi2rx_stop error. %d\n", ret); + + hwd_viif_csi2rx_uninitialize(viif_dev->hwd_res); + + return ret; +} + +/* ----- subdevice video operations ----- */ +static int visconti_viif_isp_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev; + int ret; + + if (enable) { + ret = viif_csi2rx_initialize(viif_dev); + if (ret) + return ret; + return viif_csi2rx_start(viif_dev); + } else { + return viif_csi2rx_stop(viif_dev); + } +} + +/* ----- subdevice pad operations ----- */ +static int visconti_viif_isp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad == 0) { + /* sink */ + if (code->index > ARRAY_SIZE(visconti_mbus_formats) - 1) + return -EINVAL; + code->code = visconti_mbus_formats[code->index].code; + return 0; + } + + /* source */ + if (code->index > 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_YUV8_1X24; + return 0; +} + +static struct v4l2_mbus_framefmt *visconti_viif_isp_get_pad_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) +{ + struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev; + struct v4l2_subdev_state state = { + .pads = viif_dev->isp_subdev.pad_cfg, + }; + + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, sd_state, pad); + else + return v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, &state, pad); +} + +static struct v4l2_rect *visconti_viif_isp_get_pad_crop(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) +{ + struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev; + struct v4l2_subdev_state state = { + .pads = viif_dev->isp_subdev.pad_cfg, + }; + + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(&viif_dev->isp_subdev.sd, sd_state, pad); + else + return v4l2_subdev_get_try_crop(&viif_dev->isp_subdev.sd, &state, pad); +} + +static struct v4l2_rect *visconti_viif_isp_get_pad_compose(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) +{ + struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev; + struct v4l2_subdev_state state = { + .pads = viif_dev->isp_subdev.pad_cfg, + }; + + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_compose(&viif_dev->isp_subdev.sd, sd_state, pad); + else + return v4l2_subdev_get_try_compose(&viif_dev->isp_subdev.sd, &state, pad); +} + +static int visconti_viif_isp_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev; + + mutex_lock(&viif_dev->isp_subdev.ops_lock); + fmt->format = *visconti_viif_isp_get_pad_fmt(sd, sd_state, fmt->pad, fmt->which); + mutex_unlock(&viif_dev->isp_subdev.ops_lock); + + return 0; +} + +static void visconti_viif_isp_set_sink_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_mbus_framefmt *format, u32 which) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src0_fmt, *src1_fmt, *src2_fmt; + + sink_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SINK, which); + src0_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SRC_PATH0, which); + src1_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SRC_PATH1, which); + src2_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SRC_PATH2, which); + + /* update mbus code only if it's available */ + if (viif_is_valid_mbus_code(format->code)) + sink_fmt->code = format->code; + + /* sink::mbus_code is derived from src::mbus_code */ + if (viif_get_mbus_rgb_out(sink_fmt->code)) { + src0_fmt->code = MEDIA_BUS_FMT_RGB888_1X24; + src1_fmt->code = MEDIA_BUS_FMT_RGB888_1X24; + } else { + src0_fmt->code = MEDIA_BUS_FMT_YUV8_1X24; + src1_fmt->code = MEDIA_BUS_FMT_YUV8_1X24; + } + + /* SRC2 (RAW output) follows SINK format */ + src2_fmt->code = format->code; + src2_fmt->width = format->width; + src2_fmt->height = format->height; + + /* size check */ + sink_fmt->width = format->width; + sink_fmt->height = format->height; + + *format = *sink_fmt; +} + +static void visconti_viif_isp_set_src_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_mbus_framefmt *format, unsigned int pad, + u32 which) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + struct v4l2_rect *src_crop; + + sink_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SINK, + V4L2_SUBDEV_FORMAT_ACTIVE); + src_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, pad, which); + src_crop = visconti_viif_isp_get_pad_crop(sd, sd_state, pad, which); + + /* sink::mbus_code is derived from src::mbus_code */ + if (viif_get_mbus_rgb_out(sink_fmt->code)) + src_fmt->code = MEDIA_BUS_FMT_RGB888_1X24; + else + src_fmt->code = MEDIA_BUS_FMT_YUV8_1X24; + + /*size check*/ + src_fmt->width = format->width; + src_fmt->height = format->height; + + /*update crop*/ + src_crop->width = format->width; + src_crop->height = format->height; + + *format = *src_fmt; +} + +static void visconti_viif_isp_set_src_fmt_rawpath(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_mbus_framefmt *format, + unsigned int pad, u32 which) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + + sink_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SINK, + V4L2_SUBDEV_FORMAT_ACTIVE); + src_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, pad, which); + + /* RAWPATH SRC pad has just the same configuration as SINK pad */ + src_fmt->code = sink_fmt->code; + src_fmt->width = sink_fmt->width; + src_fmt->height = sink_fmt->height; + + *format = *src_fmt; +} + +static int visconti_viif_isp_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev; + + mutex_lock(&viif_dev->isp_subdev.ops_lock); + + if (fmt->pad == VIIF_ISP_PAD_SINK) + visconti_viif_isp_set_sink_fmt(sd, sd_state, &fmt->format, fmt->which); + else if (fmt->pad == VIIF_ISP_PAD_SRC_PATH2) + visconti_viif_isp_set_src_fmt_rawpath(sd, sd_state, &fmt->format, fmt->pad, + fmt->which); + else + visconti_viif_isp_set_src_fmt(sd, sd_state, &fmt->format, fmt->pad, fmt->which); + + mutex_unlock(&viif_dev->isp_subdev.ops_lock); + + return 0; +} + +#define VISCONTI_VIIF_ISP_DEFAULT_WIDTH 1920 +#define VISCONTI_VIIF_ISP_DEFAULT_HEIGHT 1080 +#define VISCONTI_VIIF_MAX_COMPOSED_WIDTH 8190 +#define VISCONTI_VIIF_MAX_COMPOSED_HEIGHT 4094 + +static int visconti_viif_isp_init_config(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) +{ + struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev; + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + struct v4l2_rect *src_crop, *sink_compose; + + sink_fmt = + v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, sd_state, VIIF_ISP_PAD_SINK); + sink_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + sink_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + + sink_compose = + v4l2_subdev_get_try_compose(&viif_dev->isp_subdev.sd, sd_state, VIIF_ISP_PAD_SINK); + sink_compose->top = 0; + sink_compose->left = 0; + sink_compose->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + sink_compose->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + + src_fmt = v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, sd_state, + VIIF_ISP_PAD_SRC_PATH0); + src_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + src_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->code = MEDIA_BUS_FMT_YUV8_1X24; + + src_crop = v4l2_subdev_get_try_crop(&viif_dev->isp_subdev.sd, sd_state, + VIIF_ISP_PAD_SRC_PATH0); + src_crop->top = 0; + src_crop->left = 0; + src_crop->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + src_crop->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + + src_fmt = v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, sd_state, + VIIF_ISP_PAD_SRC_PATH1); + src_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + src_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->code = MEDIA_BUS_FMT_YUV8_1X24; + + src_crop = v4l2_subdev_get_try_crop(&viif_dev->isp_subdev.sd, sd_state, + VIIF_ISP_PAD_SRC_PATH1); + src_crop->top = 0; + src_crop->left = 0; + src_crop->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + src_crop->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + + src_fmt = v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, sd_state, + VIIF_ISP_PAD_SRC_PATH2); + src_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + src_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + + return 0; +} + +static int visconti_viif_isp_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev; + struct v4l2_mbus_framefmt *sink_fmt; + int ret = -EINVAL; + + mutex_lock(&viif_dev->isp_subdev.ops_lock); + if (sel->pad == VIIF_ISP_PAD_SINK) { + /* SINK PAD */ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sink_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SINK, + sel->which); + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = sink_fmt->width; + sel->r.height = sink_fmt->height; + ret = 0; + break; + case V4L2_SEL_TGT_COMPOSE: + sel->r = *visconti_viif_isp_get_pad_compose(sd, sd_state, VIIF_ISP_PAD_SINK, + sel->which); + ret = 0; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + /* fixed value */ + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = VISCONTI_VIIF_MAX_COMPOSED_WIDTH; + sel->r.height = VISCONTI_VIIF_MAX_COMPOSED_HEIGHT; + ret = 0; + break; + } + } else if ((sel->pad == VIIF_ISP_PAD_SRC_PATH0) || (sel->pad == VIIF_ISP_PAD_SRC_PATH1)) { + /* SRC PAD */ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = + *visconti_viif_isp_get_pad_crop(sd, sd_state, sel->pad, sel->which); + ret = 0; + break; + } + } + mutex_unlock(&viif_dev->isp_subdev.ops_lock); + + return ret; +} + +static int visconti_viif_isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev; + struct v4l2_mbus_framefmt *src_fmt; + struct v4l2_rect *rect, *rect_compose; + int ret = -EINVAL; + + mutex_lock(&viif_dev->isp_subdev.ops_lock); + /* only source::selection::crop is writable */ + if (sel->pad == VIIF_ISP_PAD_SRC_PATH0 || sel->pad == VIIF_ISP_PAD_SRC_PATH1) { + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + /* check if new SRC::CROP is inside SINK::COMPOSE */ + rect_compose = visconti_viif_isp_get_pad_compose( + sd, sd_state, VIIF_ISP_PAD_SINK, sel->which); + if (sel->r.top < rect_compose->top || sel->r.left < rect_compose->left || + (sel->r.top + sel->r.height) > + (rect_compose->top + rect_compose->height) || + (sel->r.left + sel->r.width) > + (rect_compose->left + rect_compose->width)) { + break; + } + + rect = visconti_viif_isp_get_pad_crop(sd, sd_state, sel->pad, sel->which); + *rect = sel->r; + + /* update SRC::FMT along with SRC::CROP */ + src_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, sel->pad, sel->which); + src_fmt->width = sel->r.width; + src_fmt->height = sel->r.height; + ret = 0; + break; + } + } + } + mutex_unlock(&viif_dev->isp_subdev.ops_lock); + + return ret; +} + +void visconti_viif_isp_set_compose_rect(struct viif_device *viif_dev, + struct viif_l2_roi_config *roi) +{ + struct v4l2_rect *rect; + + rect = visconti_viif_isp_get_pad_compose(&viif_dev->isp_subdev.sd, NULL, VIIF_ISP_PAD_SINK, + V4L2_SUBDEV_FORMAT_ACTIVE); + rect->top = 0; + rect->left = 0; + rect->width = roi->corrected_hsize[0]; + rect->height = roi->corrected_vsize[0]; +} + +static const struct media_entity_operations visconti_viif_isp_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_pad_ops visconti_viif_isp_pad_ops = { + .enum_mbus_code = visconti_viif_isp_enum_mbus_code, + .get_selection = visconti_viif_isp_get_selection, + .set_selection = visconti_viif_isp_set_selection, + .init_cfg = visconti_viif_isp_init_config, + .get_fmt = visconti_viif_isp_get_fmt, + .set_fmt = visconti_viif_isp_set_fmt, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_video_ops visconti_viif_isp_video_ops = { + .s_stream = visconti_viif_isp_s_stream, +}; + +static const struct v4l2_subdev_ops visconti_viif_isp_ops = { + .video = &visconti_viif_isp_video_ops, + .pad = &visconti_viif_isp_pad_ops, +}; + +/* ----- register/remove isp subdevice node ----- */ +int visconti_viif_isp_register(struct viif_device *viif_dev) +{ + struct v4l2_subdev_state state = { + .pads = viif_dev->isp_subdev.pad_cfg, + }; + struct media_pad *pads = viif_dev->isp_subdev.pads; + struct v4l2_subdev *sd = &viif_dev->isp_subdev.sd; + int ret; + + viif_dev->isp_subdev.viif_dev = viif_dev; + + v4l2_subdev_init(sd, &visconti_viif_isp_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->entity.ops = &visconti_viif_isp_media_ops; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; + sd->owner = THIS_MODULE; + strscpy(sd->name, "visconti-viif:isp", sizeof(sd->name)); + + pads[0].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + pads[1].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; + pads[2].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; + pads[3].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; + + mutex_init(&viif_dev->isp_subdev.ops_lock); + + ret = media_entity_pads_init(&sd->entity, 4, pads); + if (ret) { + dev_err(viif_dev->dev, "Failed on media_entity_pads_init\n"); + return ret; + } + + ret = v4l2_device_register_subdev(&viif_dev->v4l2_dev, sd); + if (ret) { + dev_err(viif_dev->dev, "Failed to resize ISP subdev\n"); + goto err_cleanup_media_entity; + } + + visconti_viif_isp_init_config(sd, &state); + + return 0; + +err_cleanup_media_entity: + media_entity_cleanup(&sd->entity); + return ret; +} + +void visconti_viif_isp_unregister(struct viif_device *viif_dev) +{ + v4l2_device_unregister_subdev(&viif_dev->isp_subdev.sd); + media_entity_cleanup(&viif_dev->isp_subdev.sd.entity); +} From patchwork Wed Jan 11 02:24:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuji Ishikawa X-Patchwork-Id: 641551 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DB25FC54EBC for ; Wed, 11 Jan 2023 02:30:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235730AbjAKCai (ORCPT ); Tue, 10 Jan 2023 21:30:38 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42376 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229970AbjAKCag (ORCPT ); Tue, 10 Jan 2023 21:30:36 -0500 Received: from mo-csw.securemx.jp (mo-csw1514.securemx.jp [210.130.202.153]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 77C0A6473; Tue, 10 Jan 2023 18:30:33 -0800 (PST) Received: by mo-csw.securemx.jp (mx-mo-csw1514) id 30B2U2Ec014671; Wed, 11 Jan 2023 11:30:02 +0900 X-Iguazu-Qid: 34tMdphy6Txolz2ilU X-Iguazu-QSIG: v=2; s=0; t=1673404201; q=34tMdphy6Txolz2ilU; m=ebj7RwJ/dTzbx575Bl82AXM2gELlkU7fLp2PoSCkVdQ= Received: from imx12-a.toshiba.co.jp ([38.106.60.135]) by relay.securemx.jp (mx-mr1513) id 30B2U01V037260 (version=TLSv1.2 cipher=AES128-GCM-SHA256 bits=128 verify=NOT); Wed, 11 Jan 2023 11:30:00 +0900 X-SA-MID: 48878253 From: Yuji Ishikawa To: Hans Verkuil , Laurent Pinchart , Mauro Carvalho Chehab , Nobuhiro Iwamatsu , Rob Herring , Krzysztof Kozlowski , "Rafael J . Wysocki" , Mark Brown Cc: linux-media@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, yuji2.ishikawa@toshiba.co.jp Subject: [PATCH v5 4/6] media: platform: visconti: Add Toshiba Visconti Video Input Interface driver v4l2 controls handler Date: Wed, 11 Jan 2023 11:24:31 +0900 X-TSB-HOP2: ON Message-Id: <20230111022433.25950-5-yuji2.ishikawa@toshiba.co.jp> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230111022433.25950-1-yuji2.ishikawa@toshiba.co.jp> References: <20230111022433.25950-1-yuji2.ishikawa@toshiba.co.jp> Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add support to Image Signal Processors of Visconti's Video Input Interface. This patch adds vendor specific compound controls to configure the image signal processor. Signed-off-by: Yuji Ishikawa --- Changelog v2: - Resend v1 because a patch exceeds size limit. Changelog v3: - Adapted to media control framework - Introduced ISP subdevice, capture device - Remove private IOCTLs and add vendor specific V4L2 controls - Change function name avoiding camelcase and uppercase letters Changelog v4: - Split patches because the v3 patch exceeds size limit - Stop using ID number to identify driver instance: - Use dynamically allocated structure to hold HW specific context, instead of static one. - Call HW layer functions with the context structure instead of ID number Changelog v5: - no change --- drivers/media/platform/visconti/Makefile | 4 +- .../media/platform/visconti/hwd_viif_l1isp.c | 2674 +++++++++++++++++ .../media/platform/visconti/viif_controls.c | 1153 +++++++ drivers/media/platform/visconti/viif_isp.c | 2 + 4 files changed, 3831 insertions(+), 2 deletions(-) create mode 100644 drivers/media/platform/visconti/hwd_viif_l1isp.c create mode 100644 drivers/media/platform/visconti/viif_controls.c diff --git a/drivers/media/platform/visconti/Makefile b/drivers/media/platform/visconti/Makefile index d7a23c1f4e8..13cf70ce309 100644 --- a/drivers/media/platform/visconti/Makefile +++ b/drivers/media/platform/visconti/Makefile @@ -3,7 +3,7 @@ # Makefile for the Visconti video input device driver # -visconti-viif-objs = viif.o viif_capture.o viif_isp.o -visconti-viif-objs += hwd_viif_csi2rx.o hwd_viif.o +visconti-viif-objs = viif.o viif_capture.o viif_controls.o viif_isp.o +visconti-viif-objs += hwd_viif_csi2rx.o hwd_viif.o hwd_viif_l1isp.o obj-$(CONFIG_VIDEO_VISCONTI_VIIF) += visconti-viif.o diff --git a/drivers/media/platform/visconti/hwd_viif_l1isp.c b/drivers/media/platform/visconti/hwd_viif_l1isp.c new file mode 100644 index 00000000000..882eea92205 --- /dev/null +++ b/drivers/media/platform/visconti/hwd_viif_l1isp.c @@ -0,0 +1,2674 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2022 TOSHIBA CORPORATION + * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include "hwd_viif.h" +#include "hwd_viif_internal.h" + +/** + * hwd_viif_l1_set_input_mode() - Configure L1ISP input mode. + * + * @mode: L1ISP preprocessing mode @ref hwd_viif_l1_input_mode + * @depth: input color depth (even only) + * - [8..24] in case of mode = #HWD_VIIF_L1_INPUT_HDR or #HWD_VIIF_L1_INPUT_HDR_IMG_CORRECT + * - [8..14] in case of mode = #HWD_VIIF_L1_INPUT_PWL or #HWD_VIIF_L1_INPUT_PWL_IMG_CORRECT + * - [8..12] in case of mode = #HWD_VIIF_L1_INPUT_SDR + * @raw_color_filter: RAW color filter array @ref hwd_viif_l1_raw_color_filter_mode + * @interpolation_order: interpolation order for input image + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "mode" is out of range + * - "depth" is out of range + * - "raw_color_filter" is out of range + * - "interpolation_order" is NULL in case of "mode" == #HWD_VIIF_L1_INPUT_SDR + * - "interpolation_order" is not NULL in case of "mode" != #HWD_VIIF_L1_INPUT_SDR + * + * Note that if 'mode' is not HWD_VIIF_L1_INPUT_SDR, NULL shall be set to 'interpolation_order'. + */ +s32 hwd_viif_l1_set_input_mode(struct hwd_viif_res *res, u32 mode, u32 depth, u32 raw_color_filter) +{ + u32 depth_max; + + if (mode >= HWD_VIIF_L1_INPUT_MODE_NUM || mode == HWD_VIIF_L1_INPUT_SDR) + return -EINVAL; + + if (mode == HWD_VIIF_L1_INPUT_PWL || mode == HWD_VIIF_L1_INPUT_PWL_IMG_CORRECT) + depth_max = HWD_VIIF_L1_INPUT_DEPTH_PWL_MAX; + else + depth_max = HWD_VIIF_L1_INPUT_DEPTH_MAX; + + if (depth < HWD_VIIF_L1_INPUT_DEPTH_MIN || depth > depth_max || ((depth % 2U) != 0U) || + raw_color_filter >= HWD_VIIF_L1_RAW_MODE_NUM) { + return -EINVAL; + } + + writel(mode, &res->capture_reg->l1isp.L1_SYSM_INPUT_MODE); + writel(depth, &res->capture_reg->l1isp.L1_IBUF_DEPTH); + writel(raw_color_filter, &res->capture_reg->l1isp.L1_SYSM_START_COLOR); + + return 0; +} + +/** + * hwd_viif_l1_set_rgb_to_y_coef() - Configure L1ISP RGB coefficients to calculate Y. + * + * @coef_r: R coefficient to calculate Y [256..65024] accuracy: 1/65536 + * @coef_g: G coefficient to calculate Y [256..65024] accuracy: 1/65536 + * @coef_b: B coefficient to calculate Y [256..65024] accuracy: 1/65536 + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "coef_r" is out of range + * - "coef_g" is out of range + * - "coef_b" is out of range + * + * Note that it is possible that coef_r/g/b has rounding error when the value is set to HW register + */ +s32 hwd_viif_l1_set_rgb_to_y_coef(struct hwd_viif_res *res, u16 coef_r, u16 coef_g, u16 coef_b) +{ + if (coef_r < HWD_VIIF_L1_COEF_MIN || coef_r > HWD_VIIF_L1_COEF_MAX || + coef_g < HWD_VIIF_L1_COEF_MIN || coef_g > HWD_VIIF_L1_COEF_MAX || + coef_b < HWD_VIIF_L1_COEF_MIN || coef_b > HWD_VIIF_L1_COEF_MAX) { + return -EINVAL; + } + + writel((u32)coef_r, &res->capture_reg->l1isp.L1_SYSM_YCOEF_R); + writel((u32)coef_g, &res->capture_reg->l1isp.L1_SYSM_YCOEF_G); + writel((u32)coef_b, &res->capture_reg->l1isp.L1_SYSM_YCOEF_B); + + return 0; +} + +/** + * hwd_viif_l1_set_ag_mode() - Configure L1ISP AG mode. + * + * @param: pointer to struct hwd_viif_l1_ag_mode + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "param" is NULL + * - each member of "param" is invalid + */ +s32 hwd_viif_l1_set_ag_mode(struct hwd_viif_res *res, const struct viif_l1_ag_mode_config *param) +{ + u32 val; + + if (!param || param->sysm_ag_psel_hobc_high >= HWD_VIIF_L1_AG_ID_NUM || + param->sysm_ag_psel_hobc_middle_led >= HWD_VIIF_L1_AG_ID_NUM || + param->sysm_ag_psel_hobc_low >= HWD_VIIF_L1_AG_ID_NUM || + param->sysm_ag_psel_abpc_high >= HWD_VIIF_L1_AG_ID_NUM || + param->sysm_ag_psel_abpc_middle_led >= HWD_VIIF_L1_AG_ID_NUM || + param->sysm_ag_psel_abpc_low >= HWD_VIIF_L1_AG_ID_NUM || + param->sysm_ag_psel_rcnr_high >= HWD_VIIF_L1_AG_ID_NUM || + param->sysm_ag_psel_rcnr_middle_led >= HWD_VIIF_L1_AG_ID_NUM || + param->sysm_ag_psel_rcnr_low >= HWD_VIIF_L1_AG_ID_NUM || + param->sysm_ag_ssel_lssc >= HWD_VIIF_L1_SENSITIVITY_IMAGE_NUM || + param->sysm_ag_psel_lssc >= HWD_VIIF_L1_AG_ID_NUM || + param->sysm_ag_ssel_mpro >= HWD_VIIF_L1_SENSITIVITY_IMAGE_NUM || + param->sysm_ag_psel_mpro >= HWD_VIIF_L1_AG_ID_NUM || + param->sysm_ag_ssel_vpro >= HWD_VIIF_L1_SENSITIVITY_IMAGE_NUM || + param->sysm_ag_psel_vpro >= HWD_VIIF_L1_AG_ID_NUM || + (param->sysm_ag_cont_hobc_en_high != HWD_VIIF_ENABLE && + param->sysm_ag_cont_hobc_en_high != HWD_VIIF_DISABLE) || + (param->sysm_ag_cont_hobc_en_middle_led != HWD_VIIF_ENABLE && + param->sysm_ag_cont_hobc_en_middle_led != HWD_VIIF_DISABLE) || + (param->sysm_ag_cont_hobc_en_low != HWD_VIIF_ENABLE && + param->sysm_ag_cont_hobc_en_low != HWD_VIIF_DISABLE) || + (param->sysm_ag_cont_rcnr_en_high != HWD_VIIF_ENABLE && + param->sysm_ag_cont_rcnr_en_high != HWD_VIIF_DISABLE) || + (param->sysm_ag_cont_rcnr_en_middle_led != HWD_VIIF_ENABLE && + param->sysm_ag_cont_rcnr_en_middle_led != HWD_VIIF_DISABLE) || + (param->sysm_ag_cont_rcnr_en_low != HWD_VIIF_ENABLE && + param->sysm_ag_cont_rcnr_en_low != HWD_VIIF_DISABLE) || + (param->sysm_ag_cont_lssc_en != HWD_VIIF_ENABLE && + param->sysm_ag_cont_lssc_en != HWD_VIIF_DISABLE) || + (param->sysm_ag_cont_mpro_en != HWD_VIIF_ENABLE && + param->sysm_ag_cont_mpro_en != HWD_VIIF_DISABLE) || + (param->sysm_ag_cont_vpro_en != HWD_VIIF_ENABLE && + param->sysm_ag_cont_vpro_en != HWD_VIIF_DISABLE) || + (param->sysm_ag_cont_abpc_en_middle_led != HWD_VIIF_ENABLE && + param->sysm_ag_cont_abpc_en_middle_led != HWD_VIIF_DISABLE) || + (param->sysm_ag_cont_abpc_en_high != HWD_VIIF_ENABLE && + param->sysm_ag_cont_abpc_en_high != HWD_VIIF_DISABLE) || + (param->sysm_ag_cont_abpc_en_low != HWD_VIIF_ENABLE && + param->sysm_ag_cont_abpc_en_low != HWD_VIIF_DISABLE)) { + return -EINVAL; + } + + /* SYSM_AG_PARAM */ + val = ((u32)param->sysm_ag_grad[0] << 16U) | ((u32)param->sysm_ag_ofst[0]); + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_PARAM_A); + val = ((u32)param->sysm_ag_grad[1] << 16U) | ((u32)param->sysm_ag_ofst[1]); + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_PARAM_B); + val = ((u32)param->sysm_ag_grad[2] << 16U) | ((u32)param->sysm_ag_ofst[2]); + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_PARAM_C); + val = ((u32)param->sysm_ag_grad[3] << 16U) | ((u32)param->sysm_ag_ofst[3]); + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_PARAM_D); + + /* SYSM_AG_SEL */ + val = ((u32)param->sysm_ag_psel_hobc_high << 6U) | + ((u32)param->sysm_ag_psel_hobc_middle_led << 4U) | + ((u32)param->sysm_ag_psel_hobc_low << 2U); + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_SEL_HOBC); + + val = ((u32)param->sysm_ag_psel_abpc_high << 6U) | + ((u32)param->sysm_ag_psel_abpc_middle_led << 4U) | + ((u32)param->sysm_ag_psel_abpc_low << 2U); + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_SEL_ABPC); + + val = ((u32)param->sysm_ag_psel_rcnr_high << 6U) | + ((u32)param->sysm_ag_psel_rcnr_middle_led << 4U) | + ((u32)param->sysm_ag_psel_rcnr_low << 2U); + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_SEL_RCNR); + + val = ((u32)param->sysm_ag_ssel_lssc << 2U) | ((u32)param->sysm_ag_psel_lssc); + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_SEL_LSSC); + + val = ((u32)param->sysm_ag_ssel_mpro << 2U) | ((u32)param->sysm_ag_psel_mpro); + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_SEL_MPRO); + + val = ((u32)param->sysm_ag_ssel_vpro << 2U) | ((u32)param->sysm_ag_psel_vpro); + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_SEL_VPRO); + + /* SYSM_AG_CONT */ + val = (param->sysm_ag_cont_hobc_en_middle_led << 24U) | + ((u32)(param->sysm_ag_cont_hobc_test_middle_led) << 16U) | + (param->sysm_ag_cont_hobc_en_high << 8U) | (u32)param->sysm_ag_cont_hobc_test_high; + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_CONT_HOBC01_EN); + val = (param->sysm_ag_cont_hobc_en_low << 8U) | (u32)param->sysm_ag_cont_hobc_test_low; + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_CONT_HOBC2_EN); + + val = (param->sysm_ag_cont_abpc_en_middle_led << 24U) | + ((u32)(param->sysm_ag_cont_abpc_test_middle_led) << 16U) | + (param->sysm_ag_cont_abpc_en_high << 8U) | (u32)param->sysm_ag_cont_abpc_test_high; + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_CONT_ABPC01_EN); + val = (param->sysm_ag_cont_abpc_en_low << 8U) | (u32)param->sysm_ag_cont_abpc_test_low; + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_CONT_ABPC2_EN); + + val = (param->sysm_ag_cont_rcnr_en_middle_led << 24U) | + ((u32)(param->sysm_ag_cont_rcnr_test_middle_led) << 16U) | + (param->sysm_ag_cont_rcnr_en_high << 8U) | (u32)param->sysm_ag_cont_rcnr_test_high; + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_CONT_RCNR01_EN); + val = (param->sysm_ag_cont_rcnr_en_low << 8U) | (u32)param->sysm_ag_cont_rcnr_test_low; + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_CONT_RCNR2_EN); + + val = (param->sysm_ag_cont_lssc_en << 8U) | (u32)param->sysm_ag_cont_lssc_test; + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_CONT_LSSC_EN); + + val = (param->sysm_ag_cont_mpro_en << 8U) | (u32)param->sysm_ag_cont_mpro_test; + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_CONT_MPRO_EN); + + val = (param->sysm_ag_cont_vpro_en << 8U) | (u32)param->sysm_ag_cont_vpro_test; + writel(val, &res->capture_reg->l1isp.L1_SYSM_AG_CONT_VPRO_EN); + + return 0; +} + +/** + * hwd_viif_l1_set_ag() - Configure L1ISP analog gain. + * + * @gain_h: analog gain value for high sensitivity image [0..65535] + * @gain_m: analog gain value for middle sensitivity or led image [0..65535] + * @gain_l: analog gain value for low sensitivity image [0..65535] + * Return: 0 operation completed successfully + */ +s32 hwd_viif_l1_set_ag(struct hwd_viif_res *res, u16 gain_h, u16 gain_m, u16 gain_l) +{ + writel((u32)gain_h, &res->capture_reg->l1isp.L1_SYSM_AG_H); + writel((u32)gain_m, &res->capture_reg->l1isp.L1_SYSM_AG_M); + writel((u32)gain_l, &res->capture_reg->l1isp.L1_SYSM_AG_L); + + return 0; +} + +/** + * hwd_viif_l1_set_hdre() - Configure L1ISP HDR extension parameters. + * + * @param: pointer to struct hwd_viif_l1_hdre + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "param" is NULL + * - each member of "param" is invalid + */ +s32 hwd_viif_l1_set_hdre(struct hwd_viif_res *res, const struct viif_l1_hdre_config *param) +{ + u32 idx; + + if (!param) + return -EINVAL; + + for (idx = 0; idx < 16U; idx++) { + if (param->hdre_src_point[idx] > HWD_VIIF_L1_HDRE_MAX_KNEEPOINT_VAL) + return -EINVAL; + } + + for (idx = 0; idx < 17U; idx++) { + if (param->hdre_dst_base[idx] > HWD_VIIF_L1_HDRE_MAX_HDRE_SIG_VAL || + param->hdre_ratio[idx] >= HWD_VIIF_L1_HDRE_MAX_OUT_PIXEL_RATIO) { + return -EINVAL; + } + } + + if (param->hdre_dst_max_val > HWD_VIIF_L1_HDRE_MAX_OUT_PIXEL_VAL) + return -EINVAL; + + writel(param->hdre_src_point[0], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT00); + writel(param->hdre_src_point[1], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT01); + writel(param->hdre_src_point[2], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT02); + writel(param->hdre_src_point[3], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT03); + writel(param->hdre_src_point[4], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT04); + writel(param->hdre_src_point[5], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT05); + writel(param->hdre_src_point[6], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT06); + writel(param->hdre_src_point[7], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT07); + writel(param->hdre_src_point[8], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT08); + writel(param->hdre_src_point[9], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT09); + writel(param->hdre_src_point[10], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT10); + writel(param->hdre_src_point[11], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT11); + writel(param->hdre_src_point[12], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT12); + writel(param->hdre_src_point[13], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT13); + writel(param->hdre_src_point[14], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT14); + writel(param->hdre_src_point[15], &res->capture_reg->l1isp.L1_HDRE_SRCPOINT15); + + writel(0, &res->capture_reg->l1isp.L1_HDRE_SRCBASE00); + writel(param->hdre_src_point[0], &res->capture_reg->l1isp.L1_HDRE_SRCBASE01); + writel(param->hdre_src_point[1], &res->capture_reg->l1isp.L1_HDRE_SRCBASE02); + writel(param->hdre_src_point[2], &res->capture_reg->l1isp.L1_HDRE_SRCBASE03); + writel(param->hdre_src_point[3], &res->capture_reg->l1isp.L1_HDRE_SRCBASE04); + writel(param->hdre_src_point[4], &res->capture_reg->l1isp.L1_HDRE_SRCBASE05); + writel(param->hdre_src_point[5], &res->capture_reg->l1isp.L1_HDRE_SRCBASE06); + writel(param->hdre_src_point[6], &res->capture_reg->l1isp.L1_HDRE_SRCBASE07); + writel(param->hdre_src_point[7], &res->capture_reg->l1isp.L1_HDRE_SRCBASE08); + writel(param->hdre_src_point[8], &res->capture_reg->l1isp.L1_HDRE_SRCBASE09); + writel(param->hdre_src_point[9], &res->capture_reg->l1isp.L1_HDRE_SRCBASE10); + writel(param->hdre_src_point[10], &res->capture_reg->l1isp.L1_HDRE_SRCBASE11); + writel(param->hdre_src_point[11], &res->capture_reg->l1isp.L1_HDRE_SRCBASE12); + writel(param->hdre_src_point[12], &res->capture_reg->l1isp.L1_HDRE_SRCBASE13); + writel(param->hdre_src_point[13], &res->capture_reg->l1isp.L1_HDRE_SRCBASE14); + writel(param->hdre_src_point[14], &res->capture_reg->l1isp.L1_HDRE_SRCBASE15); + writel(param->hdre_src_point[15], &res->capture_reg->l1isp.L1_HDRE_SRCBASE16); + + writel(param->hdre_dst_base[0], &res->capture_reg->l1isp.L1_HDRE_DSTBASE00); + writel(param->hdre_dst_base[1], &res->capture_reg->l1isp.L1_HDRE_DSTBASE01); + writel(param->hdre_dst_base[2], &res->capture_reg->l1isp.L1_HDRE_DSTBASE02); + writel(param->hdre_dst_base[3], &res->capture_reg->l1isp.L1_HDRE_DSTBASE03); + writel(param->hdre_dst_base[4], &res->capture_reg->l1isp.L1_HDRE_DSTBASE04); + writel(param->hdre_dst_base[5], &res->capture_reg->l1isp.L1_HDRE_DSTBASE05); + writel(param->hdre_dst_base[6], &res->capture_reg->l1isp.L1_HDRE_DSTBASE06); + writel(param->hdre_dst_base[7], &res->capture_reg->l1isp.L1_HDRE_DSTBASE07); + writel(param->hdre_dst_base[8], &res->capture_reg->l1isp.L1_HDRE_DSTBASE08); + writel(param->hdre_dst_base[9], &res->capture_reg->l1isp.L1_HDRE_DSTBASE09); + writel(param->hdre_dst_base[10], &res->capture_reg->l1isp.L1_HDRE_DSTBASE10); + writel(param->hdre_dst_base[11], &res->capture_reg->l1isp.L1_HDRE_DSTBASE11); + writel(param->hdre_dst_base[12], &res->capture_reg->l1isp.L1_HDRE_DSTBASE12); + writel(param->hdre_dst_base[13], &res->capture_reg->l1isp.L1_HDRE_DSTBASE13); + writel(param->hdre_dst_base[14], &res->capture_reg->l1isp.L1_HDRE_DSTBASE14); + writel(param->hdre_dst_base[15], &res->capture_reg->l1isp.L1_HDRE_DSTBASE15); + writel(param->hdre_dst_base[16], &res->capture_reg->l1isp.L1_HDRE_DSTBASE16); + + writel(param->hdre_ratio[0], &res->capture_reg->l1isp.L1_HDRE_RATIO00); + writel(param->hdre_ratio[1], &res->capture_reg->l1isp.L1_HDRE_RATIO01); + writel(param->hdre_ratio[2], &res->capture_reg->l1isp.L1_HDRE_RATIO02); + writel(param->hdre_ratio[3], &res->capture_reg->l1isp.L1_HDRE_RATIO03); + writel(param->hdre_ratio[4], &res->capture_reg->l1isp.L1_HDRE_RATIO04); + writel(param->hdre_ratio[5], &res->capture_reg->l1isp.L1_HDRE_RATIO05); + writel(param->hdre_ratio[6], &res->capture_reg->l1isp.L1_HDRE_RATIO06); + writel(param->hdre_ratio[7], &res->capture_reg->l1isp.L1_HDRE_RATIO07); + writel(param->hdre_ratio[8], &res->capture_reg->l1isp.L1_HDRE_RATIO08); + writel(param->hdre_ratio[9], &res->capture_reg->l1isp.L1_HDRE_RATIO09); + writel(param->hdre_ratio[10], &res->capture_reg->l1isp.L1_HDRE_RATIO10); + writel(param->hdre_ratio[11], &res->capture_reg->l1isp.L1_HDRE_RATIO11); + writel(param->hdre_ratio[12], &res->capture_reg->l1isp.L1_HDRE_RATIO12); + writel(param->hdre_ratio[13], &res->capture_reg->l1isp.L1_HDRE_RATIO13); + writel(param->hdre_ratio[14], &res->capture_reg->l1isp.L1_HDRE_RATIO14); + writel(param->hdre_ratio[15], &res->capture_reg->l1isp.L1_HDRE_RATIO15); + writel(param->hdre_ratio[16], &res->capture_reg->l1isp.L1_HDRE_RATIO16); + + writel(param->hdre_dst_max_val, &res->capture_reg->l1isp.L1_HDRE_DSTMAXVAL); + + return 0; +} + +/** + * hwd_viif_l1_set_img_extraction() - Configure L1ISP image extraction parameters. + * + * @input_black_gr: black level of Gr input pixel [0x0..0xffffff] + * @input_black_r: black level of R input pixel [0x0..0xffffff] + * @input_black_b: black level of B input pixel [0x0..0xffffff] + * @input_black_gb: black level of Gb input pixel [0x0..0xffffff] + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "input_black_gr" is out of range + * - "input_black_r" is out of range + * - "input_black_b" is out of range + * - "input_black_gb" is out of range + */ +s32 hwd_viif_l1_set_img_extraction(struct hwd_viif_res *res, u32 input_black_gr, u32 input_black_r, + u32 input_black_b, u32 input_black_gb) +{ + if (input_black_gr > HWD_VIIF_L1_IMG_EXTRACT_MAX_BLACK_LEVEL_VAL || + input_black_r > HWD_VIIF_L1_IMG_EXTRACT_MAX_BLACK_LEVEL_VAL || + input_black_b > HWD_VIIF_L1_IMG_EXTRACT_MAX_BLACK_LEVEL_VAL || + input_black_gb > HWD_VIIF_L1_IMG_EXTRACT_MAX_BLACK_LEVEL_VAL) { + return -EINVAL; + } + + writel(input_black_gr, &res->capture_reg->l1isp.L1_SLIC_SRCBLACKLEVEL_GR); + writel(input_black_r, &res->capture_reg->l1isp.L1_SLIC_SRCBLACKLEVEL_R); + writel(input_black_b, &res->capture_reg->l1isp.L1_SLIC_SRCBLACKLEVEL_B); + writel(input_black_gb, &res->capture_reg->l1isp.L1_SLIC_SRCBLACKLEVEL_GB); + + return 0; +} + +/** + * hwd_viif_l1_set_dpc() - Configure L1ISP defect pixel correction parameters. + * + * @param_h: pointer to defect pixel correction parameters for high sensitivity image + * @param_m: pointer to defect pixel correction parameters for middle sensitivity or led image + * @param_l: pointer to defect pixel correction parameters for low sensitivity image + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "param_h", "param_m" and "param_l" are NULL + * - each member of "param_h" is invalid + * - each member of "param_m" is invalid + * - each member of "param_l" is invalid + */ +s32 hwd_viif_l1_set_dpc(struct hwd_viif_res *res, const struct viif_l1_dpc *param_h, + const struct viif_l1_dpc *param_m, const struct viif_l1_dpc *param_l) +{ + const struct viif_l1_dpc *param; + u32 idx; + u32 val; + + if (!param_h && !param_m && !param_l) + return -EINVAL; + + for (idx = 0U; idx < 3U; idx++) { + if (idx == 0U) + param = param_h; + else if (idx == 1U) + param = param_m; + else + param = param_l; + + if (!param) + continue; + + if ((param->abpc_sta_en != HWD_VIIF_ENABLE && + param->abpc_sta_en != HWD_VIIF_DISABLE) || + (param->abpc_dyn_en != HWD_VIIF_ENABLE && + param->abpc_dyn_en != HWD_VIIF_DISABLE)) { + return -EINVAL; + } + + if (param->abpc_dyn_en != HWD_VIIF_ENABLE) + continue; + + if ((param->abpc_dyn_mode != HWD_VIIF_L1_DPC_1PIXEL && + param->abpc_dyn_mode != HWD_VIIF_L1_DPC_2PIXEL) || + param->abpc_ratio_limit > HWD_VIIF_L1_DPC_MAX_RATIO_LIMIT_VAL || + param->abpc_dark_limit > HWD_VIIF_L1_DPC_MAX_RATIO_LIMIT_VAL || + param->abpc_sn_coef_w_ag_min < HWD_VIIF_L1_DPC_MIN_LUMA_ADJ_VAL || + param->abpc_sn_coef_w_ag_min > HWD_VIIF_L1_DPC_MAX_LUMA_ADJ_VAL || + param->abpc_sn_coef_w_ag_mid < HWD_VIIF_L1_DPC_MIN_LUMA_ADJ_VAL || + param->abpc_sn_coef_w_ag_mid > HWD_VIIF_L1_DPC_MAX_LUMA_ADJ_VAL || + param->abpc_sn_coef_w_ag_max < HWD_VIIF_L1_DPC_MIN_LUMA_ADJ_VAL || + param->abpc_sn_coef_w_ag_max > HWD_VIIF_L1_DPC_MAX_LUMA_ADJ_VAL || + param->abpc_sn_coef_b_ag_min < HWD_VIIF_L1_DPC_MIN_LUMA_ADJ_VAL || + param->abpc_sn_coef_b_ag_min > HWD_VIIF_L1_DPC_MAX_LUMA_ADJ_VAL || + param->abpc_sn_coef_b_ag_mid < HWD_VIIF_L1_DPC_MIN_LUMA_ADJ_VAL || + param->abpc_sn_coef_b_ag_mid > HWD_VIIF_L1_DPC_MAX_LUMA_ADJ_VAL || + param->abpc_sn_coef_b_ag_max < HWD_VIIF_L1_DPC_MIN_LUMA_ADJ_VAL || + param->abpc_sn_coef_b_ag_max > HWD_VIIF_L1_DPC_MAX_LUMA_ADJ_VAL || + param->abpc_sn_coef_w_th_min >= param->abpc_sn_coef_w_th_max || + param->abpc_sn_coef_b_th_min >= param->abpc_sn_coef_b_th_max) { + return -EINVAL; + } + } + + val = 0; + if (param_h) + val |= param_h->abpc_sta_en << 24U; + + if (param_m) + val |= param_m->abpc_sta_en << 16U; + + if (param_l) + val |= param_l->abpc_sta_en << 8U; + + writel(val, &res->capture_reg->l1isp.L1_ABPC012_STA_EN); + + val = 0; + if (param_h) + val |= param_h->abpc_dyn_en << 24U; + + if (param_m) + val |= param_m->abpc_dyn_en << 16U; + + if (param_l) + val |= param_l->abpc_dyn_en << 8U; + + writel(val, &res->capture_reg->l1isp.L1_ABPC012_DYN_EN); + + val = 0; + if (param_h) + val |= param_h->abpc_dyn_mode << 24U; + + if (param_m) + val |= param_m->abpc_dyn_mode << 16U; + + if (param_l) + val |= param_l->abpc_dyn_mode << 8U; + + writel(val, &res->capture_reg->l1isp.L1_ABPC012_DYN_MODE); + + if (param_h) { + writel(param_h->abpc_ratio_limit, &res->capture_reg->l1isp.L1_ABPC0_RATIO_LIMIT); + writel(param_h->abpc_dark_limit, &res->capture_reg->l1isp.L1_ABPC0_DARK_LIMIT); + writel(param_h->abpc_sn_coef_w_ag_min, + &res->capture_reg->l1isp.L1_ABPC0_SN_COEF_W_AG_MIN); + writel(param_h->abpc_sn_coef_w_ag_mid, + &res->capture_reg->l1isp.L1_ABPC0_SN_COEF_W_AG_MID); + writel(param_h->abpc_sn_coef_w_ag_max, + &res->capture_reg->l1isp.L1_ABPC0_SN_COEF_W_AG_MAX); + writel(param_h->abpc_sn_coef_b_ag_min, + &res->capture_reg->l1isp.L1_ABPC0_SN_COEF_B_AG_MIN); + writel(param_h->abpc_sn_coef_b_ag_mid, + &res->capture_reg->l1isp.L1_ABPC0_SN_COEF_B_AG_MID); + writel(param_h->abpc_sn_coef_b_ag_max, + &res->capture_reg->l1isp.L1_ABPC0_SN_COEF_B_AG_MAX); + writel((u32)param_h->abpc_sn_coef_w_th_min, + &res->capture_reg->l1isp.L1_ABPC0_SN_COEF_W_TH_MIN); + writel((u32)param_h->abpc_sn_coef_w_th_max, + &res->capture_reg->l1isp.L1_ABPC0_SN_COEF_W_TH_MAX); + writel((u32)param_h->abpc_sn_coef_b_th_min, + &res->capture_reg->l1isp.L1_ABPC0_SN_COEF_B_TH_MIN); + writel((u32)param_h->abpc_sn_coef_b_th_max, + &res->capture_reg->l1isp.L1_ABPC0_SN_COEF_B_TH_MAX); + } + + if (param_m) { + writel(param_m->abpc_ratio_limit, &res->capture_reg->l1isp.L1_ABPC1_RATIO_LIMIT); + writel(param_m->abpc_dark_limit, &res->capture_reg->l1isp.L1_ABPC1_DARK_LIMIT); + writel(param_m->abpc_sn_coef_w_ag_min, + &res->capture_reg->l1isp.L1_ABPC1_SN_COEF_W_AG_MIN); + writel(param_m->abpc_sn_coef_w_ag_mid, + &res->capture_reg->l1isp.L1_ABPC1_SN_COEF_W_AG_MID); + writel(param_m->abpc_sn_coef_w_ag_max, + &res->capture_reg->l1isp.L1_ABPC1_SN_COEF_W_AG_MAX); + writel(param_m->abpc_sn_coef_b_ag_min, + &res->capture_reg->l1isp.L1_ABPC1_SN_COEF_B_AG_MIN); + writel(param_m->abpc_sn_coef_b_ag_mid, + &res->capture_reg->l1isp.L1_ABPC1_SN_COEF_B_AG_MID); + writel(param_m->abpc_sn_coef_b_ag_max, + &res->capture_reg->l1isp.L1_ABPC1_SN_COEF_B_AG_MAX); + writel((u32)param_m->abpc_sn_coef_w_th_min, + &res->capture_reg->l1isp.L1_ABPC1_SN_COEF_W_TH_MIN); + writel((u32)param_m->abpc_sn_coef_w_th_max, + &res->capture_reg->l1isp.L1_ABPC1_SN_COEF_W_TH_MAX); + writel((u32)param_m->abpc_sn_coef_b_th_min, + &res->capture_reg->l1isp.L1_ABPC1_SN_COEF_B_TH_MIN); + writel((u32)param_m->abpc_sn_coef_b_th_max, + &res->capture_reg->l1isp.L1_ABPC1_SN_COEF_B_TH_MAX); + } + + if (param_l) { + writel(param_l->abpc_ratio_limit, &res->capture_reg->l1isp.L1_ABPC2_RATIO_LIMIT); + writel(param_l->abpc_dark_limit, &res->capture_reg->l1isp.L1_ABPC2_DARK_LIMIT); + writel(param_l->abpc_sn_coef_w_ag_min, + &res->capture_reg->l1isp.L1_ABPC2_SN_COEF_W_AG_MIN); + writel(param_l->abpc_sn_coef_w_ag_mid, + &res->capture_reg->l1isp.L1_ABPC2_SN_COEF_W_AG_MID); + writel(param_l->abpc_sn_coef_w_ag_max, + &res->capture_reg->l1isp.L1_ABPC2_SN_COEF_W_AG_MAX); + writel(param_l->abpc_sn_coef_b_ag_min, + &res->capture_reg->l1isp.L1_ABPC2_SN_COEF_B_AG_MIN); + writel(param_l->abpc_sn_coef_b_ag_mid, + &res->capture_reg->l1isp.L1_ABPC2_SN_COEF_B_AG_MID); + writel(param_l->abpc_sn_coef_b_ag_max, + &res->capture_reg->l1isp.L1_ABPC2_SN_COEF_B_AG_MAX); + writel((u32)param_l->abpc_sn_coef_w_th_min, + &res->capture_reg->l1isp.L1_ABPC2_SN_COEF_W_TH_MIN); + writel((u32)param_l->abpc_sn_coef_w_th_max, + &res->capture_reg->l1isp.L1_ABPC2_SN_COEF_W_TH_MAX); + writel((u32)param_l->abpc_sn_coef_b_th_min, + &res->capture_reg->l1isp.L1_ABPC2_SN_COEF_B_TH_MIN); + writel((u32)param_l->abpc_sn_coef_b_th_max, + &res->capture_reg->l1isp.L1_ABPC2_SN_COEF_B_TH_MAX); + } + + return 0; +} + +/** + * hwd_viif_l1_set_dpc_table_transmission() - + * Configure L1ISP transferring defect pixel correction table. + * + * @table_h: defect pixel correction table for high sensitivity image(physical address) + * @table_m: defect pixel correction table for middle sensitivity or led image(physical address) + * @table_l: defect pixel correction table for low sensitivity image(physical address) + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "table_h", "table_m" or "table_l" is not 8byte alignment + * + * Note that when 0 is set to table address, table transfer of the table is disabled. + */ +s32 hwd_viif_l1_set_dpc_table_transmission(struct hwd_viif_res *res, uintptr_t table_h, + uintptr_t table_m, uintptr_t table_l) +{ + u32 val = 0x0U; + + if (((table_h % HWD_VIIF_L1_VDM_ALIGN) != 0U) || + ((table_m % HWD_VIIF_L1_VDM_ALIGN) != 0U) || + ((table_l % HWD_VIIF_L1_VDM_ALIGN) != 0U)) { + return -EINVAL; + } + + /* VDM common settings */ + + writel(HWD_VIIF_L1_VDM_CFG_PARAM, &res->capture_reg->vdm.t_group[0].VDM_T_CFG); + writel(HWD_VIIF_L1_VDM_SRAM_BASE, &res->capture_reg->vdm.t_group[0].VDM_T_SRAM_BASE); + writel(HWD_VIIF_L1_VDM_SRAM_SIZE, &res->capture_reg->vdm.t_group[0].VDM_T_SRAM_SIZE); + + if (table_h != 0U) { + writel((u32)table_h, &res->capture_reg->vdm.t_port[0].VDM_T_STADR); + writel(HWD_VIIF_L1_VDM_DPC_TABLE_SIZE, &res->capture_reg->vdm.t_port[0].VDM_T_SIZE); + val |= 0x1U; + } + + if (table_m != 0U) { + writel((u32)table_m, &res->capture_reg->vdm.t_port[1].VDM_T_STADR); + writel(HWD_VIIF_L1_VDM_DPC_TABLE_SIZE, &res->capture_reg->vdm.t_port[1].VDM_T_SIZE); + val |= 0x2U; + } + + if (table_l != 0U) { + writel((u32)table_l, &res->capture_reg->vdm.t_port[2].VDM_T_STADR); + writel(HWD_VIIF_L1_VDM_DPC_TABLE_SIZE, &res->capture_reg->vdm.t_port[2].VDM_T_SIZE); + val |= 0x4U; + } + + val |= (readl(&res->capture_reg->vdm.VDM_T_ENABLE) & 0xfffffff8U); + writel(val, &res->capture_reg->vdm.VDM_T_ENABLE); + + return 0; +} + +/** + * hwd_viif_l1_set_preset_white_balance() - Configure L1ISP preset white balance parameters. + * + * @dstmaxval: maximum output pixel value [0..4095] + * @param_h: pointer to preset white balance parameters for high sensitivity image + * @param_m: pointer to preset white balance parameters for middle sensitivity or led image + * @param_l: pointer to preset white balance parameters for low sensitivity image + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "dstmaxval" is out of range + * - "param_h", "param_m", and "param_l" are NULL + * - each parameter of "param_h" is out of range + * - each parameter of "param_m" is out of range + * - each parameter of "param_l" is out of range + * Note that when NULL is set to "param_{h/m/l}", the corresponding parameters are not set to HW. + */ +s32 hwd_viif_l1_set_preset_white_balance(struct hwd_viif_res *res, u32 dstmaxval, + const struct viif_l1_preset_wb *param_h, + const struct viif_l1_preset_wb *param_m, + const struct viif_l1_preset_wb *param_l) +{ + if (dstmaxval > HWD_VIIF_L1_PWHB_MAX_OUT_PIXEL_VAL || (!param_h && !param_m && !param_l)) + return -EINVAL; + + if (param_h) { + if (param_h->gain_gr >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL || + param_h->gain_r >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL || + param_h->gain_b >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL || + param_h->gain_gb >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL) { + return -EINVAL; + } + } + + if (param_m) { + if (param_m->gain_gr >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL || + param_m->gain_r >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL || + param_m->gain_b >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL || + param_m->gain_gb >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL) { + return -EINVAL; + } + } + + if (param_l) { + if (param_l->gain_gr >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL || + param_l->gain_r >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL || + param_l->gain_b >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL || + param_l->gain_gb >= HWD_VIIF_L1_PWHB_MAX_GAIN_VAL) { + return -EINVAL; + } + } + + writel(dstmaxval, &res->capture_reg->l1isp.L1_PWHB_DSTMAXVAL); + + if (param_h) { + writel(param_h->gain_gr, &res->capture_reg->l1isp.L1_PWHB_H_GR); + writel(param_h->gain_r, &res->capture_reg->l1isp.L1_PWHB_HR); + writel(param_h->gain_b, &res->capture_reg->l1isp.L1_PWHB_HB); + writel(param_h->gain_gb, &res->capture_reg->l1isp.L1_PWHB_H_GB); + } + + if (param_m) { + writel(param_m->gain_gr, &res->capture_reg->l1isp.L1_PWHB_M_GR); + writel(param_m->gain_r, &res->capture_reg->l1isp.L1_PWHB_MR); + writel(param_m->gain_b, &res->capture_reg->l1isp.L1_PWHB_MB); + writel(param_m->gain_gb, &res->capture_reg->l1isp.L1_PWHB_M_GB); + } + + if (param_l) { + writel(param_l->gain_gr, &res->capture_reg->l1isp.L1_PWHB_L_GR); + writel(param_l->gain_r, &res->capture_reg->l1isp.L1_PWHB_LR); + writel(param_l->gain_b, &res->capture_reg->l1isp.L1_PWHB_LB); + writel(param_l->gain_gb, &res->capture_reg->l1isp.L1_PWHB_L_GB); + } + + return 0; +} + +/** + * hwd_viif_l1_set_raw_color_noise_reduction() - + * Configure L1ISP raw color noise reduction parameters. + * + * @param_h: pointer to raw color noise reduction parameters for high sensitivity image + * @param_m: pointer to raw color noise reduction parameters for middle sensitivity or led image + * @param_l: pointer to raw color noise reduction parameters for low sensitivity image + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "param_h", "param_m", and "param_l" are NULL + * - each parameter of "param_h" is out of range + * - each parameter of "param_m" is out of range + * - each parameter of "param_l" is out of range + * Note that when NULL is set to "param_{h/m/l}", the corresponding parameters are not set to HW. + */ +s32 hwd_viif_l1_set_raw_color_noise_reduction( + struct hwd_viif_res *res, const struct viif_l1_raw_color_noise_reduction *param_h, + const struct viif_l1_raw_color_noise_reduction *param_m, + const struct viif_l1_raw_color_noise_reduction *param_l) +{ + const struct viif_l1_raw_color_noise_reduction *param; + u32 idx; + + if (!param_h && !param_m && !param_l) + return -EINVAL; + + for (idx = 0; idx < 3U; idx++) { + if (idx == 0U) + param = param_h; + else if (idx == 1U) + param = param_m; + else + param = param_l; + + if (!param) + continue; + + if (param->rcnr_sw != HWD_VIIF_ENABLE && param->rcnr_sw != HWD_VIIF_DISABLE) + return -EINVAL; + + if (param->rcnr_cnf_dark_ag0 > HWD_VIIF_L1_RCNR_MAX_DARK_ADJUSTMENT_VAL || + param->rcnr_cnf_dark_ag1 > HWD_VIIF_L1_RCNR_MAX_DARK_ADJUSTMENT_VAL || + param->rcnr_cnf_dark_ag2 > HWD_VIIF_L1_RCNR_MAX_DARK_ADJUSTMENT_VAL || + param->rcnr_cnf_ratio_ag0 > HWD_VIIF_L1_RCNR_MAX_LUMA_LINKAGE_ADJUSTMENT_VAL || + param->rcnr_cnf_ratio_ag1 > HWD_VIIF_L1_RCNR_MAX_LUMA_LINKAGE_ADJUSTMENT_VAL || + param->rcnr_cnf_ratio_ag2 > HWD_VIIF_L1_RCNR_MAX_LUMA_LINKAGE_ADJUSTMENT_VAL || + param->rcnr_cnf_clip_gain_r > HWD_VIIF_L1_RCNR_MAX_ADJUSTMENT_GAIN_VAL || + param->rcnr_cnf_clip_gain_g > HWD_VIIF_L1_RCNR_MAX_ADJUSTMENT_GAIN_VAL || + param->rcnr_cnf_clip_gain_b > HWD_VIIF_L1_RCNR_MAX_ADJUSTMENT_GAIN_VAL || + param->rcnr_a1l_dark_ag0 > HWD_VIIF_L1_RCNR_MAX_DARK_ADJUSTMENT_VAL || + param->rcnr_a1l_dark_ag1 > HWD_VIIF_L1_RCNR_MAX_DARK_ADJUSTMENT_VAL || + param->rcnr_a1l_dark_ag2 > HWD_VIIF_L1_RCNR_MAX_DARK_ADJUSTMENT_VAL || + param->rcnr_a1l_ratio_ag0 > HWD_VIIF_L1_RCNR_MAX_LUMA_LINKAGE_ADJUSTMENT_VAL || + param->rcnr_a1l_ratio_ag1 > HWD_VIIF_L1_RCNR_MAX_LUMA_LINKAGE_ADJUSTMENT_VAL || + param->rcnr_a1l_ratio_ag2 > HWD_VIIF_L1_RCNR_MAX_LUMA_LINKAGE_ADJUSTMENT_VAL || + param->rcnr_inf_zero_clip > HWD_VIIF_L1_RCNR_MAX_ZERO_CLIP_VAL || + param->rcnr_merge_d2blend_ag0 > HWD_VIIF_L1_RCNR_MAX_BLEND_VAL || + param->rcnr_merge_d2blend_ag1 > HWD_VIIF_L1_RCNR_MAX_BLEND_VAL || + param->rcnr_merge_d2blend_ag2 > HWD_VIIF_L1_RCNR_MAX_BLEND_VAL || + param->rcnr_merge_black > HWD_VIIF_L1_RCNR_MAX_BLACK_LEVEL_VAL || + param->rcnr_merge_mindiv < HWD_VIIF_L1_RCNR_MIN_0DIV_GUARD_VAL || + param->rcnr_merge_mindiv > HWD_VIIF_L1_RCNR_MAX_0DIV_GUARD_VAL) { + return -EINVAL; + } + + switch (param->rcnr_hry_type) { + case HWD_VIIF_L1_RCNR_LOW_RESOLUTION: + case HWD_VIIF_L1_RCNR_MIDDLE_RESOLUTION: + case HWD_VIIF_L1_RCNR_HIGH_RESOLUTION: + case HWD_VIIF_L1_RCNR_ULTRA_HIGH_RESOLUTION: + break; + default: + return -EINVAL; + } + + if (param->rcnr_anf_blend_ag0 != HWD_VIIF_L1_MSF_BLEND_RATIO_0_DIV_64 && + param->rcnr_anf_blend_ag0 != HWD_VIIF_L1_MSF_BLEND_RATIO_1_DIV_64 && + param->rcnr_anf_blend_ag0 != HWD_VIIF_L1_MSF_BLEND_RATIO_2_DIV_64) { + return -EINVAL; + } + if (param->rcnr_anf_blend_ag1 != HWD_VIIF_L1_MSF_BLEND_RATIO_0_DIV_64 && + param->rcnr_anf_blend_ag1 != HWD_VIIF_L1_MSF_BLEND_RATIO_1_DIV_64 && + param->rcnr_anf_blend_ag1 != HWD_VIIF_L1_MSF_BLEND_RATIO_2_DIV_64) { + return -EINVAL; + } + if (param->rcnr_anf_blend_ag2 != HWD_VIIF_L1_MSF_BLEND_RATIO_0_DIV_64 && + param->rcnr_anf_blend_ag2 != HWD_VIIF_L1_MSF_BLEND_RATIO_1_DIV_64 && + param->rcnr_anf_blend_ag2 != HWD_VIIF_L1_MSF_BLEND_RATIO_2_DIV_64) { + return -EINVAL; + } + + if (param->rcnr_lpf_threshold >= HWD_VIIF_L1_RCNR_MAX_CALC_MSF_NOISE_MULTI_VAL || + param->rcnr_merge_hlblend_ag0 > HWD_VIIF_L1_RCNR_MAX_GEN_LUMA_SIG_BLEND_VAL || + param->rcnr_merge_hlblend_ag1 > HWD_VIIF_L1_RCNR_MAX_GEN_LUMA_SIG_BLEND_VAL || + param->rcnr_merge_hlblend_ag2 > HWD_VIIF_L1_RCNR_MAX_GEN_LUMA_SIG_BLEND_VAL || + (param->rcnr_gnr_sw != HWD_VIIF_DISABLE && + param->rcnr_gnr_sw != HWD_VIIF_ENABLE)) { + return -EINVAL; + } + + if (param->rcnr_gnr_sw == HWD_VIIF_ENABLE) { + if (param->rcnr_gnr_ratio > HWD_VIIF_L1_RCNR_MAX_UP_LIMIT_GRGB_SENS_RATIO) + return -EINVAL; + if (param->rcnr_gnr_wide_en != HWD_VIIF_DISABLE && + param->rcnr_gnr_wide_en != HWD_VIIF_ENABLE) { + return -EINVAL; + } + } + } + + if (param_h) { + writel(param_h->rcnr_sw, &res->capture_reg->l1isp.L1_RCNR0_SW); + + writel(param_h->rcnr_cnf_dark_ag0, &res->capture_reg->l1isp.L1_RCNR0_CNF_DARK_AG0); + writel(param_h->rcnr_cnf_dark_ag1, &res->capture_reg->l1isp.L1_RCNR0_CNF_DARK_AG1); + writel(param_h->rcnr_cnf_dark_ag2, &res->capture_reg->l1isp.L1_RCNR0_CNF_DARK_AG2); + + writel(param_h->rcnr_cnf_ratio_ag0, + &res->capture_reg->l1isp.L1_RCNR0_CNF_RATIO_AG0); + writel(param_h->rcnr_cnf_ratio_ag1, + &res->capture_reg->l1isp.L1_RCNR0_CNF_RATIO_AG1); + writel(param_h->rcnr_cnf_ratio_ag2, + &res->capture_reg->l1isp.L1_RCNR0_CNF_RATIO_AG2); + + writel(param_h->rcnr_cnf_clip_gain_r, + &res->capture_reg->l1isp.L1_RCNR0_CNF_CLIP_GAIN_R); + writel(param_h->rcnr_cnf_clip_gain_g, + &res->capture_reg->l1isp.L1_RCNR0_CNF_CLIP_GAIN_G); + writel(param_h->rcnr_cnf_clip_gain_b, + &res->capture_reg->l1isp.L1_RCNR0_CNF_CLIP_GAIN_B); + + writel(param_h->rcnr_a1l_dark_ag0, &res->capture_reg->l1isp.L1_RCNR0_A1L_DARK_AG0); + writel(param_h->rcnr_a1l_dark_ag1, &res->capture_reg->l1isp.L1_RCNR0_A1L_DARK_AG1); + writel(param_h->rcnr_a1l_dark_ag2, &res->capture_reg->l1isp.L1_RCNR0_A1L_DARK_AG2); + + writel(param_h->rcnr_a1l_ratio_ag0, + &res->capture_reg->l1isp.L1_RCNR0_A1L_RATIO_AG0); + writel(param_h->rcnr_a1l_ratio_ag1, + &res->capture_reg->l1isp.L1_RCNR0_A1L_RATIO_AG1); + writel(param_h->rcnr_a1l_ratio_ag2, + &res->capture_reg->l1isp.L1_RCNR0_A1L_RATIO_AG2); + + writel(param_h->rcnr_inf_zero_clip, + &res->capture_reg->l1isp.L1_RCNR0_INF_ZERO_CLIP); + + writel(param_h->rcnr_merge_d2blend_ag0, + &res->capture_reg->l1isp.L1_RCNR0_MERGE_D2BLEND_AG0); + writel(param_h->rcnr_merge_d2blend_ag1, + &res->capture_reg->l1isp.L1_RCNR0_MERGE_D2BLEND_AG1); + writel(param_h->rcnr_merge_d2blend_ag2, + &res->capture_reg->l1isp.L1_RCNR0_MERGE_D2BLEND_AG2); + writel(param_h->rcnr_merge_black, &res->capture_reg->l1isp.L1_RCNR0_MERGE_BLACK); + writel(param_h->rcnr_merge_mindiv, &res->capture_reg->l1isp.L1_RCNR0_MERGE_MINDIV); + + writel(param_h->rcnr_hry_type, &res->capture_reg->l1isp.L1_RCNR0_HRY_TYPE); + + writel(param_h->rcnr_anf_blend_ag0, + &res->capture_reg->l1isp.L1_RCNR0_ANF_BLEND_AG0); + writel(param_h->rcnr_anf_blend_ag1, + &res->capture_reg->l1isp.L1_RCNR0_ANF_BLEND_AG1); + writel(param_h->rcnr_anf_blend_ag2, + &res->capture_reg->l1isp.L1_RCNR0_ANF_BLEND_AG2); + + writel(param_h->rcnr_lpf_threshold, + &res->capture_reg->l1isp.L1_RCNR0_LPF_THRESHOLD); + + writel(param_h->rcnr_merge_hlblend_ag0, + &res->capture_reg->l1isp.L1_RCNR0_MERGE_HLBLEND_AG0); + writel(param_h->rcnr_merge_hlblend_ag1, + &res->capture_reg->l1isp.L1_RCNR0_MERGE_HLBLEND_AG1); + writel(param_h->rcnr_merge_hlblend_ag2, + &res->capture_reg->l1isp.L1_RCNR0_MERGE_HLBLEND_AG2); + + writel(param_h->rcnr_gnr_sw, &res->capture_reg->l1isp.L1_RCNR0_GNR_SW); + + if (param_h->rcnr_gnr_sw == HWD_VIIF_ENABLE) { + writel(param_h->rcnr_gnr_ratio, + &res->capture_reg->l1isp.L1_RCNR0_GNR_RATIO); + writel(param_h->rcnr_gnr_wide_en, + &res->capture_reg->l1isp.L1_RCNR0_GNR_WIDE_EN); + } + } + + if (param_m) { + writel(param_m->rcnr_sw, &res->capture_reg->l1isp.L1_RCNR1_SW); + + writel(param_m->rcnr_cnf_dark_ag0, &res->capture_reg->l1isp.L1_RCNR1_CNF_DARK_AG0); + writel(param_m->rcnr_cnf_dark_ag1, &res->capture_reg->l1isp.L1_RCNR1_CNF_DARK_AG1); + writel(param_m->rcnr_cnf_dark_ag2, &res->capture_reg->l1isp.L1_RCNR1_CNF_DARK_AG2); + + writel(param_m->rcnr_cnf_ratio_ag0, + &res->capture_reg->l1isp.L1_RCNR1_CNF_RATIO_AG0); + writel(param_m->rcnr_cnf_ratio_ag1, + &res->capture_reg->l1isp.L1_RCNR1_CNF_RATIO_AG1); + writel(param_m->rcnr_cnf_ratio_ag2, + &res->capture_reg->l1isp.L1_RCNR1_CNF_RATIO_AG2); + + writel(param_m->rcnr_cnf_clip_gain_r, + &res->capture_reg->l1isp.L1_RCNR1_CNF_CLIP_GAIN_R); + writel(param_m->rcnr_cnf_clip_gain_g, + &res->capture_reg->l1isp.L1_RCNR1_CNF_CLIP_GAIN_G); + writel(param_m->rcnr_cnf_clip_gain_b, + &res->capture_reg->l1isp.L1_RCNR1_CNF_CLIP_GAIN_B); + + writel(param_m->rcnr_a1l_dark_ag0, &res->capture_reg->l1isp.L1_RCNR1_A1L_DARK_AG0); + writel(param_m->rcnr_a1l_dark_ag1, &res->capture_reg->l1isp.L1_RCNR1_A1L_DARK_AG1); + writel(param_m->rcnr_a1l_dark_ag2, &res->capture_reg->l1isp.L1_RCNR1_A1L_DARK_AG2); + + writel(param_m->rcnr_a1l_ratio_ag0, + &res->capture_reg->l1isp.L1_RCNR1_A1L_RATIO_AG0); + writel(param_m->rcnr_a1l_ratio_ag1, + &res->capture_reg->l1isp.L1_RCNR1_A1L_RATIO_AG1); + writel(param_m->rcnr_a1l_ratio_ag2, + &res->capture_reg->l1isp.L1_RCNR1_A1L_RATIO_AG2); + + writel(param_m->rcnr_inf_zero_clip, + &res->capture_reg->l1isp.L1_RCNR1_INF_ZERO_CLIP); + + writel(param_m->rcnr_merge_d2blend_ag0, + &res->capture_reg->l1isp.L1_RCNR1_MERGE_D2BLEND_AG0); + writel(param_m->rcnr_merge_d2blend_ag1, + &res->capture_reg->l1isp.L1_RCNR1_MERGE_D2BLEND_AG1); + writel(param_m->rcnr_merge_d2blend_ag2, + &res->capture_reg->l1isp.L1_RCNR1_MERGE_D2BLEND_AG2); + writel(param_m->rcnr_merge_black, &res->capture_reg->l1isp.L1_RCNR1_MERGE_BLACK); + writel(param_m->rcnr_merge_mindiv, &res->capture_reg->l1isp.L1_RCNR1_MERGE_MINDIV); + + writel(param_m->rcnr_hry_type, &res->capture_reg->l1isp.L1_RCNR1_HRY_TYPE); + + writel(param_m->rcnr_anf_blend_ag0, + &res->capture_reg->l1isp.L1_RCNR1_ANF_BLEND_AG0); + writel(param_m->rcnr_anf_blend_ag1, + &res->capture_reg->l1isp.L1_RCNR1_ANF_BLEND_AG1); + writel(param_m->rcnr_anf_blend_ag2, + &res->capture_reg->l1isp.L1_RCNR1_ANF_BLEND_AG2); + + writel(param_m->rcnr_lpf_threshold, + &res->capture_reg->l1isp.L1_RCNR1_LPF_THRESHOLD); + + writel(param_m->rcnr_merge_hlblend_ag0, + &res->capture_reg->l1isp.L1_RCNR1_MERGE_HLBLEND_AG0); + writel(param_m->rcnr_merge_hlblend_ag1, + &res->capture_reg->l1isp.L1_RCNR1_MERGE_HLBLEND_AG1); + writel(param_m->rcnr_merge_hlblend_ag2, + &res->capture_reg->l1isp.L1_RCNR1_MERGE_HLBLEND_AG2); + + writel(param_m->rcnr_gnr_sw, &res->capture_reg->l1isp.L1_RCNR1_GNR_SW); + + if (param_m->rcnr_gnr_sw == HWD_VIIF_ENABLE) { + writel(param_m->rcnr_gnr_ratio, + &res->capture_reg->l1isp.L1_RCNR1_GNR_RATIO); + writel(param_m->rcnr_gnr_wide_en, + &res->capture_reg->l1isp.L1_RCNR1_GNR_WIDE_EN); + } + } + + if (param_l) { + writel(param_l->rcnr_sw, &res->capture_reg->l1isp.L1_RCNR2_SW); + + writel(param_l->rcnr_cnf_dark_ag0, &res->capture_reg->l1isp.L1_RCNR2_CNF_DARK_AG0); + writel(param_l->rcnr_cnf_dark_ag1, &res->capture_reg->l1isp.L1_RCNR2_CNF_DARK_AG1); + writel(param_l->rcnr_cnf_dark_ag2, &res->capture_reg->l1isp.L1_RCNR2_CNF_DARK_AG2); + + writel(param_l->rcnr_cnf_ratio_ag0, + &res->capture_reg->l1isp.L1_RCNR2_CNF_RATIO_AG0); + writel(param_l->rcnr_cnf_ratio_ag1, + &res->capture_reg->l1isp.L1_RCNR2_CNF_RATIO_AG1); + writel(param_l->rcnr_cnf_ratio_ag2, + &res->capture_reg->l1isp.L1_RCNR2_CNF_RATIO_AG2); + + writel(param_l->rcnr_cnf_clip_gain_r, + &res->capture_reg->l1isp.L1_RCNR2_CNF_CLIP_GAIN_R); + writel(param_l->rcnr_cnf_clip_gain_g, + &res->capture_reg->l1isp.L1_RCNR2_CNF_CLIP_GAIN_G); + writel(param_l->rcnr_cnf_clip_gain_b, + &res->capture_reg->l1isp.L1_RCNR2_CNF_CLIP_GAIN_B); + + writel(param_l->rcnr_a1l_dark_ag0, &res->capture_reg->l1isp.L1_RCNR2_A1L_DARK_AG0); + writel(param_l->rcnr_a1l_dark_ag1, &res->capture_reg->l1isp.L1_RCNR2_A1L_DARK_AG1); + writel(param_l->rcnr_a1l_dark_ag2, &res->capture_reg->l1isp.L1_RCNR2_A1L_DARK_AG2); + + writel(param_l->rcnr_a1l_ratio_ag0, + &res->capture_reg->l1isp.L1_RCNR2_A1L_RATIO_AG0); + writel(param_l->rcnr_a1l_ratio_ag1, + &res->capture_reg->l1isp.L1_RCNR2_A1L_RATIO_AG1); + writel(param_l->rcnr_a1l_ratio_ag2, + &res->capture_reg->l1isp.L1_RCNR2_A1L_RATIO_AG2); + + writel(param_l->rcnr_inf_zero_clip, + &res->capture_reg->l1isp.L1_RCNR2_INF_ZERO_CLIP); + + writel(param_l->rcnr_merge_d2blend_ag0, + &res->capture_reg->l1isp.L1_RCNR2_MERGE_D2BLEND_AG0); + writel(param_l->rcnr_merge_d2blend_ag1, + &res->capture_reg->l1isp.L1_RCNR2_MERGE_D2BLEND_AG1); + writel(param_l->rcnr_merge_d2blend_ag2, + &res->capture_reg->l1isp.L1_RCNR2_MERGE_D2BLEND_AG2); + writel(param_l->rcnr_merge_black, &res->capture_reg->l1isp.L1_RCNR2_MERGE_BLACK); + writel(param_l->rcnr_merge_mindiv, &res->capture_reg->l1isp.L1_RCNR2_MERGE_MINDIV); + + writel(param_l->rcnr_hry_type, &res->capture_reg->l1isp.L1_RCNR2_HRY_TYPE); + + writel(param_l->rcnr_anf_blend_ag0, + &res->capture_reg->l1isp.L1_RCNR2_ANF_BLEND_AG0); + writel(param_l->rcnr_anf_blend_ag1, + &res->capture_reg->l1isp.L1_RCNR2_ANF_BLEND_AG1); + writel(param_l->rcnr_anf_blend_ag2, + &res->capture_reg->l1isp.L1_RCNR2_ANF_BLEND_AG2); + + writel(param_l->rcnr_lpf_threshold, + &res->capture_reg->l1isp.L1_RCNR2_LPF_THRESHOLD); + + writel(param_l->rcnr_merge_hlblend_ag0, + &res->capture_reg->l1isp.L1_RCNR2_MERGE_HLBLEND_AG0); + writel(param_l->rcnr_merge_hlblend_ag1, + &res->capture_reg->l1isp.L1_RCNR2_MERGE_HLBLEND_AG1); + writel(param_l->rcnr_merge_hlblend_ag2, + &res->capture_reg->l1isp.L1_RCNR2_MERGE_HLBLEND_AG2); + + writel(param_l->rcnr_gnr_sw, &res->capture_reg->l1isp.L1_RCNR2_GNR_SW); + + if (param_l->rcnr_gnr_sw == HWD_VIIF_ENABLE) { + writel(param_l->rcnr_gnr_ratio, + &res->capture_reg->l1isp.L1_RCNR2_GNR_RATIO); + writel(param_l->rcnr_gnr_wide_en, + &res->capture_reg->l1isp.L1_RCNR2_GNR_WIDE_EN); + } + } + + return 0; +} + +/** + * hwd_viif_l1_set_hdrs() - Configure L1ISP HDR synthesis parameters. + * + * @param: pointer to HDR synthesis parameters + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "param" is NULL + * - each parameter of "param" is out of range + */ +s32 hwd_viif_l1_set_hdrs(struct hwd_viif_res *res, const struct viif_l1_hdrs_config *param) +{ + if (!param || + (param->hdrs_hdr_mode != HWD_VIIF_L1_HDRS_NOT_USE_MIDDLE_SENS_IMAGE && + param->hdrs_hdr_mode != HWD_VIIF_L1_HDRS_USE_MIDDLE_SENS_IMAGE) || + param->hdrs_hdr_ratio_m < HWD_VIIF_L1_HDRS_MIN_BLEND_RATIO || + param->hdrs_hdr_ratio_m > HWD_VIIF_L1_HDRS_MAX_BLEND_RATIO || + param->hdrs_hdr_ratio_l < HWD_VIIF_L1_HDRS_MIN_BLEND_RATIO || + param->hdrs_hdr_ratio_l > HWD_VIIF_L1_HDRS_MAX_BLEND_RATIO || + param->hdrs_hdr_ratio_e < HWD_VIIF_L1_HDRS_MIN_BLEND_RATIO || + param->hdrs_hdr_ratio_e > HWD_VIIF_L1_HDRS_MAX_BLEND_RATIO || + param->hdrs_dg_h >= HWD_VIIF_L1_HDRS_MAX_DIGITAL_GAIN_VAL || + param->hdrs_dg_m >= HWD_VIIF_L1_HDRS_MAX_DIGITAL_GAIN_VAL || + param->hdrs_dg_l >= HWD_VIIF_L1_HDRS_MAX_DIGITAL_GAIN_VAL || + param->hdrs_dg_e >= HWD_VIIF_L1_HDRS_MAX_DIGITAL_GAIN_VAL || + param->hdrs_blendend_h > HWD_VIIF_L1_HDRS_MAX_BLEND_PIX_VAL || + param->hdrs_blendend_m > HWD_VIIF_L1_HDRS_MAX_BLEND_PIX_VAL || + param->hdrs_blendend_e > HWD_VIIF_L1_HDRS_MAX_BLEND_PIX_VAL || + param->hdrs_blendbeg_h > HWD_VIIF_L1_HDRS_MAX_BLEND_PIX_VAL || + param->hdrs_blendbeg_m > HWD_VIIF_L1_HDRS_MAX_BLEND_PIX_VAL || + param->hdrs_blendbeg_e > HWD_VIIF_L1_HDRS_MAX_BLEND_PIX_VAL || + (param->hdrs_led_mode_on != HWD_VIIF_ENABLE && + param->hdrs_led_mode_on != HWD_VIIF_DISABLE) || + param->hdrs_dst_max_val > HWD_VIIF_L1_HDRS_MAX_DST_MAX_VAL) { + return -EINVAL; + } + + writel(param->hdrs_hdr_mode, &res->capture_reg->l1isp.L1_HDRS_HDRMODE); + + writel(param->hdrs_hdr_ratio_m, &res->capture_reg->l1isp.L1_HDRS_HDRRATIO_M); + writel(param->hdrs_hdr_ratio_l, &res->capture_reg->l1isp.L1_HDRS_HDRRATIO_L); + writel(param->hdrs_hdr_ratio_e, &res->capture_reg->l1isp.L1_HDRS_HDRRATIO_E); + + writel(param->hdrs_dg_h, &res->capture_reg->l1isp.L1_HDRS_DG_H); + writel(param->hdrs_dg_m, &res->capture_reg->l1isp.L1_HDRS_DG_M); + writel(param->hdrs_dg_l, &res->capture_reg->l1isp.L1_HDRS_DG_L); + writel(param->hdrs_dg_e, &res->capture_reg->l1isp.L1_HDRS_DG_E); + + writel(param->hdrs_blendend_h, &res->capture_reg->l1isp.L1_HDRS_BLENDEND_H); + writel(param->hdrs_blendend_m, &res->capture_reg->l1isp.L1_HDRS_BLENDEND_M); + writel(param->hdrs_blendend_e, &res->capture_reg->l1isp.L1_HDRS_BLENDEND_E); + + writel(param->hdrs_blendbeg_h, &res->capture_reg->l1isp.L1_HDRS_BLENDBEG_H); + writel(param->hdrs_blendbeg_m, &res->capture_reg->l1isp.L1_HDRS_BLENDBEG_M); + writel(param->hdrs_blendbeg_e, &res->capture_reg->l1isp.L1_HDRS_BLENDBEG_E); + + writel(param->hdrs_led_mode_on, &res->capture_reg->l1isp.L1_HDRS_LEDMODE_ON); + writel(param->hdrs_dst_max_val, &res->capture_reg->l1isp.L1_HDRS_DSTMAXVAL); + + return 0; +} + +/** + * hwd_viif_l1_set_black_level_correction() - Configure L1ISP black level correction parameters. + * + * @param: pointer to black level correction parameters + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "param" is NULL + * - each parameter of "param" is out of range + */ +s32 hwd_viif_l1_set_black_level_correction( + struct hwd_viif_res *res, const struct viif_l1_black_level_correction_config *param) +{ + if (!param || param->srcblacklevel_gr > HWD_VIIF_L1_BLACK_LEVEL_MAX_VAL || + param->srcblacklevel_r > HWD_VIIF_L1_BLACK_LEVEL_MAX_VAL || + param->srcblacklevel_b > HWD_VIIF_L1_BLACK_LEVEL_MAX_VAL || + param->srcblacklevel_gb > HWD_VIIF_L1_BLACK_LEVEL_MAX_VAL || + param->mulval_gr >= HWD_VIIF_L1_BLACK_LEVEL_MAX_GAIN_VAL || + param->mulval_r >= HWD_VIIF_L1_BLACK_LEVEL_MAX_GAIN_VAL || + param->mulval_b >= HWD_VIIF_L1_BLACK_LEVEL_MAX_GAIN_VAL || + param->mulval_gb >= HWD_VIIF_L1_BLACK_LEVEL_MAX_GAIN_VAL || + param->dstmaxval > HWD_VIIF_L1_BLACK_LEVEL_MAX_DST_VAL) { + return -EINVAL; + } + + writel(param->srcblacklevel_gr, &res->capture_reg->l1isp.L1_BLVC_SRCBLACKLEVEL_GR); + writel(param->srcblacklevel_r, &res->capture_reg->l1isp.L1_BLVC_SRCBLACKLEVEL_R); + writel(param->srcblacklevel_b, &res->capture_reg->l1isp.L1_BLVC_SRCBLACKLEVEL_B); + writel(param->srcblacklevel_gb, &res->capture_reg->l1isp.L1_BLVC_SRCBLACKLEVELGB); + + writel(param->mulval_gr, &res->capture_reg->l1isp.L1_BLVC_MULTVAL_GR); + writel(param->mulval_r, &res->capture_reg->l1isp.L1_BLVC_MULTVAL_R); + writel(param->mulval_b, &res->capture_reg->l1isp.L1_BLVC_MULTVAL_B); + writel(param->mulval_gb, &res->capture_reg->l1isp.L1_BLVC_MULTVAL_GB); + + writel(param->dstmaxval, &res->capture_reg->l1isp.L1_BLVC_DSTMAXVAL); + + return 0; +} + +/** + * hwd_viif_l1_set_lsc() - Configure L1ISP lens shading correction parameters. + * + * @param: pointer to lens shading correction parameters + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - each parameter of "param" is out of range + * @note when NULL is set to "param" + */ +s32 hwd_viif_l1_set_lsc(struct hwd_viif_res *res, const struct hwd_viif_l1_lsc *param) +{ + u32 sysm_width, sysm_height; + u32 grid_h_size = 0U; + u32 grid_v_size = 0U; + s32 ret = 0; + u32 idx; + u32 val; + u32 tmp; + + if (!param) { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_LSSC_EN); + return 0; + } + + sysm_width = readl(&res->capture_reg->l1isp.L1_SYSM_WIDTH); + sysm_height = readl(&res->capture_reg->l1isp.L1_SYSM_HEIGHT); + + if (param->lssc_parabola_param) { + if (param->lssc_parabola_param->lssc_para_h_center >= sysm_width || + param->lssc_parabola_param->lssc_para_v_center >= sysm_height || + param->lssc_parabola_param->lssc_para_h_gain >= HWD_VIIF_LSC_MAX_GAIN || + param->lssc_parabola_param->lssc_para_v_gain >= HWD_VIIF_LSC_MAX_GAIN) { + return -EINVAL; + } + + switch (param->lssc_parabola_param->lssc_para_mgsel2) { + case HWD_VIIF_L1_PARA_COEF_GAIN_ONE_EIGHTH: + case HWD_VIIF_L1_PARA_COEF_GAIN_ONE_FOURTH: + case HWD_VIIF_L1_PARA_COEF_GAIN_ONE_SECOND: + case HWD_VIIF_L1_PARA_COEF_GAIN_ONE_FIRST: + break; + default: + return -EINVAL; + } + + switch (param->lssc_parabola_param->lssc_para_mgsel4) { + case HWD_VIIF_L1_PARA_COEF_GAIN_ONE_EIGHTH: + case HWD_VIIF_L1_PARA_COEF_GAIN_ONE_FOURTH: + case HWD_VIIF_L1_PARA_COEF_GAIN_ONE_SECOND: + case HWD_VIIF_L1_PARA_COEF_GAIN_ONE_FIRST: + break; + default: + return -EINVAL; + } + + for (idx = 0U; idx < 8U; idx++) { + const struct viif_l1_lsc_parabola_ag_param *ag_param; + + switch (idx) { + case 0U: + ag_param = ¶m->lssc_parabola_param->r_2d; + break; + case 1U: + ag_param = ¶m->lssc_parabola_param->r_4d; + break; + case 2U: + ag_param = ¶m->lssc_parabola_param->gr_2d; + break; + case 3U: + ag_param = ¶m->lssc_parabola_param->gr_4d; + break; + case 4U: + ag_param = ¶m->lssc_parabola_param->gb_2d; + break; + case 5U: + ag_param = ¶m->lssc_parabola_param->gb_4d; + break; + case 6U: + ag_param = ¶m->lssc_parabola_param->b_2d; + break; + default: + ag_param = ¶m->lssc_parabola_param->b_4d; + break; + } + + if (!ag_param || ag_param->lssc_paracoef_h_l_max < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_h_l_max >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_h_l_min < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_h_l_min >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_h_l_min > ag_param->lssc_paracoef_h_l_max || + ag_param->lssc_paracoef_h_r_max < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_h_r_max >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_h_r_min < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_h_r_min >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_h_r_min > ag_param->lssc_paracoef_h_r_max || + ag_param->lssc_paracoef_v_u_max < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_v_u_max >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_v_u_min < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_v_u_min >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_v_u_min > ag_param->lssc_paracoef_v_u_max || + ag_param->lssc_paracoef_v_d_max < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_v_d_max >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_v_d_min < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_v_d_min >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_v_d_min > ag_param->lssc_paracoef_v_d_max || + ag_param->lssc_paracoef_hv_lu_max < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_hv_lu_max >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_hv_lu_min < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_hv_lu_min >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_hv_lu_min > ag_param->lssc_paracoef_hv_lu_max || + ag_param->lssc_paracoef_hv_ru_max < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_hv_ru_max >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_hv_ru_min < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_hv_ru_min >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_hv_ru_min > ag_param->lssc_paracoef_hv_ru_max || + ag_param->lssc_paracoef_hv_ld_max < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_hv_ld_max >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_hv_ld_min < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_hv_ld_min >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_hv_ld_min > ag_param->lssc_paracoef_hv_ld_max || + ag_param->lssc_paracoef_hv_rd_max < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_hv_rd_max >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_hv_rd_min < HWD_VIIF_LSC_MIN_GAIN || + ag_param->lssc_paracoef_hv_rd_min >= HWD_VIIF_LSC_MAX_GAIN || + ag_param->lssc_paracoef_hv_rd_min > ag_param->lssc_paracoef_hv_rd_max) { + return -EINVAL; + } + } + } + + if (param->lssc_grid_param) { + switch (param->lssc_grid_param->lssc_grid_h_size) { + case 32U: + grid_h_size = 5U; + break; + case 64U: + grid_h_size = 6U; + break; + case 128U: + grid_h_size = 7U; + break; + case 256U: + grid_h_size = 8U; + break; + case 512U: + grid_h_size = 9U; + break; + default: + ret = -EINVAL; + break; + } + + if (ret != 0) + return ret; + + switch (param->lssc_grid_param->lssc_grid_v_size) { + case 32U: + grid_v_size = 5U; + break; + case 64U: + grid_v_size = 6U; + break; + case 128U: + grid_v_size = 7U; + break; + case 256U: + grid_v_size = 8U; + break; + case 512U: + grid_v_size = 9U; + break; + default: + ret = -EINVAL; + break; + } + + if (ret != 0) + return ret; + + if (param->lssc_grid_param->lssc_grid_h_center < HWD_VIIF_LSC_GRID_MIN_COORDINATE || + param->lssc_grid_param->lssc_grid_h_center > + param->lssc_grid_param->lssc_grid_h_size) { + return -EINVAL; + } + + if (sysm_width > (param->lssc_grid_param->lssc_grid_h_center + + (param->lssc_grid_param->lssc_grid_h_size * 31U))) { + return -EINVAL; + } + + if (param->lssc_grid_param->lssc_grid_v_center < HWD_VIIF_LSC_GRID_MIN_COORDINATE || + param->lssc_grid_param->lssc_grid_v_center > + param->lssc_grid_param->lssc_grid_v_size) { + return -EINVAL; + } + + if (sysm_height > (param->lssc_grid_param->lssc_grid_v_center + + (param->lssc_grid_param->lssc_grid_v_size * 23U))) { + return -EINVAL; + } + + if (param->lssc_grid_param->lssc_grid_mgsel != HWD_VIIF_L1_GRID_COEF_GAIN_X1 && + param->lssc_grid_param->lssc_grid_mgsel != HWD_VIIF_L1_GRID_COEF_GAIN_X2) { + return -EINVAL; + } + } + + if (param->lssc_pwhb_r_gain_max >= HWD_VIIF_LSC_PWB_MAX_COEF_VAL || + param->lssc_pwhb_r_gain_min >= HWD_VIIF_LSC_PWB_MAX_COEF_VAL || + param->lssc_pwhb_r_gain_min > param->lssc_pwhb_r_gain_max || + param->lssc_pwhb_gr_gain_max >= HWD_VIIF_LSC_PWB_MAX_COEF_VAL || + param->lssc_pwhb_gr_gain_min >= HWD_VIIF_LSC_PWB_MAX_COEF_VAL || + param->lssc_pwhb_gr_gain_min > param->lssc_pwhb_gr_gain_max || + param->lssc_pwhb_gb_gain_max >= HWD_VIIF_LSC_PWB_MAX_COEF_VAL || + param->lssc_pwhb_gb_gain_min >= HWD_VIIF_LSC_PWB_MAX_COEF_VAL || + param->lssc_pwhb_gb_gain_min > param->lssc_pwhb_gb_gain_max || + param->lssc_pwhb_b_gain_max >= HWD_VIIF_LSC_PWB_MAX_COEF_VAL || + param->lssc_pwhb_b_gain_min >= HWD_VIIF_LSC_PWB_MAX_COEF_VAL || + param->lssc_pwhb_b_gain_min > param->lssc_pwhb_b_gain_max) { + return -EINVAL; + } + + /* parabola shading */ + if (param->lssc_parabola_param) { + struct viif_l1_lsc_parabola_ag_param *r_2d; + struct viif_l1_lsc_parabola_ag_param *r_4d; + struct viif_l1_lsc_parabola_ag_param *gr_2d; + struct viif_l1_lsc_parabola_ag_param *gr_4d; + struct viif_l1_lsc_parabola_ag_param *gb_2d; + struct viif_l1_lsc_parabola_ag_param *gb_4d; + struct viif_l1_lsc_parabola_ag_param *b_2d; + struct viif_l1_lsc_parabola_ag_param *b_4d; + + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_LSSC_PARA_EN); + + writel(param->lssc_parabola_param->lssc_para_h_center, + &res->capture_reg->l1isp.L1_LSSC_PARA_H_CENTER); + writel(param->lssc_parabola_param->lssc_para_v_center, + &res->capture_reg->l1isp.L1_LSSC_PARA_V_CENTER); + + writel(param->lssc_parabola_param->lssc_para_h_gain, + &res->capture_reg->l1isp.L1_LSSC_PARA_H_GAIN); + writel(param->lssc_parabola_param->lssc_para_v_gain, + &res->capture_reg->l1isp.L1_LSSC_PARA_V_GAIN); + + writel(param->lssc_parabola_param->lssc_para_mgsel2, + &res->capture_reg->l1isp.L1_LSSC_PARA_MGSEL2); + writel(param->lssc_parabola_param->lssc_para_mgsel4, + &res->capture_reg->l1isp.L1_LSSC_PARA_MGSEL4); + + /* R 2D */ + r_2d = ¶m->lssc_parabola_param->r_2d; + tmp = (u32)r_2d->lssc_paracoef_h_l_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_2d->lssc_paracoef_h_l_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_2D_H_L); + + tmp = (u32)r_2d->lssc_paracoef_h_r_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_2d->lssc_paracoef_h_r_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_2D_H_R); + + tmp = (u32)r_2d->lssc_paracoef_v_u_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_2d->lssc_paracoef_v_u_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_2D_V_U); + + tmp = (u32)r_2d->lssc_paracoef_v_d_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_2d->lssc_paracoef_v_d_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_2D_V_D); + + tmp = (u32)r_2d->lssc_paracoef_hv_lu_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_2d->lssc_paracoef_hv_lu_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_2D_HV_LU); + + tmp = (u32)r_2d->lssc_paracoef_hv_ru_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_2d->lssc_paracoef_hv_ru_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_2D_HV_RU); + + tmp = (u32)r_2d->lssc_paracoef_hv_ld_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_2d->lssc_paracoef_hv_ld_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_2D_HV_LD); + + tmp = (u32)r_2d->lssc_paracoef_hv_rd_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_2d->lssc_paracoef_hv_rd_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_2D_HV_RD); + + /* R 4D */ + r_4d = ¶m->lssc_parabola_param->r_4d; + tmp = (u32)r_4d->lssc_paracoef_h_l_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_4d->lssc_paracoef_h_l_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_4D_H_L); + + tmp = (u32)r_4d->lssc_paracoef_h_r_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_4d->lssc_paracoef_h_r_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_4D_H_R); + + tmp = (u32)r_4d->lssc_paracoef_v_u_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_4d->lssc_paracoef_v_u_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_4D_V_U); + + tmp = (u32)r_4d->lssc_paracoef_v_d_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_4d->lssc_paracoef_v_d_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_4D_V_D); + + tmp = (u32)r_4d->lssc_paracoef_hv_lu_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_4d->lssc_paracoef_hv_lu_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_4D_HV_LU); + + tmp = (u32)r_4d->lssc_paracoef_hv_ru_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_4d->lssc_paracoef_hv_ru_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_4D_HV_RU); + + tmp = (u32)r_4d->lssc_paracoef_hv_ld_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_4d->lssc_paracoef_hv_ld_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_4D_HV_LD); + + tmp = (u32)r_4d->lssc_paracoef_hv_rd_max & 0x1fffU; + val = (tmp << 16U) | (u32)(r_4d->lssc_paracoef_hv_rd_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_R_COEF_4D_HV_RD); + + /* GR 2D */ + gr_2d = ¶m->lssc_parabola_param->gr_2d; + tmp = (u32)gr_2d->lssc_paracoef_h_l_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_2d->lssc_paracoef_h_l_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_2D_H_L); + + tmp = (u32)gr_2d->lssc_paracoef_h_r_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_2d->lssc_paracoef_h_r_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_2D_H_R); + + tmp = (u32)gr_2d->lssc_paracoef_v_u_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_2d->lssc_paracoef_v_u_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_2D_V_U); + + tmp = (u32)gr_2d->lssc_paracoef_v_d_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_2d->lssc_paracoef_v_d_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_2D_V_D); + + tmp = (u32)gr_2d->lssc_paracoef_hv_lu_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_2d->lssc_paracoef_hv_lu_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_2D_HV_LU); + + tmp = (u32)gr_2d->lssc_paracoef_hv_ru_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_2d->lssc_paracoef_hv_ru_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_2D_HV_RU); + + tmp = (u32)gr_2d->lssc_paracoef_hv_ld_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_2d->lssc_paracoef_hv_ld_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_2D_HV_LD); + + tmp = (u32)gr_2d->lssc_paracoef_hv_rd_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_2d->lssc_paracoef_hv_rd_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_2D_HV_RD); + + /* GR 4D */ + gr_4d = ¶m->lssc_parabola_param->gr_4d; + tmp = (u32)gr_4d->lssc_paracoef_h_l_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_4d->lssc_paracoef_h_l_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_4D_H_L); + + tmp = (u32)gr_4d->lssc_paracoef_h_r_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_4d->lssc_paracoef_h_r_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_4D_H_R); + + tmp = (u32)gr_4d->lssc_paracoef_v_u_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_4d->lssc_paracoef_v_u_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_4D_V_U); + + tmp = (u32)gr_4d->lssc_paracoef_v_d_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_4d->lssc_paracoef_v_d_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_4D_V_D); + + tmp = (u32)gr_4d->lssc_paracoef_hv_lu_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_4d->lssc_paracoef_hv_lu_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_4D_HV_LU); + + tmp = (u32)gr_4d->lssc_paracoef_hv_ru_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_4d->lssc_paracoef_hv_ru_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_4D_HV_RU); + + tmp = (u32)gr_4d->lssc_paracoef_hv_ld_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_4d->lssc_paracoef_hv_ld_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_4D_HV_LD); + + tmp = (u32)gr_4d->lssc_paracoef_hv_rd_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gr_4d->lssc_paracoef_hv_rd_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GR_COEF_4D_HV_RD); + + /* GB 2D */ + gb_2d = ¶m->lssc_parabola_param->gb_2d; + tmp = (u32)gb_2d->lssc_paracoef_h_l_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_2d->lssc_paracoef_h_l_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_2D_H_L); + + tmp = (u32)gb_2d->lssc_paracoef_h_r_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_2d->lssc_paracoef_h_r_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_2D_H_R); + + tmp = (u32)gb_2d->lssc_paracoef_v_u_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_2d->lssc_paracoef_v_u_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_2D_V_U); + + tmp = (u32)gb_2d->lssc_paracoef_v_d_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_2d->lssc_paracoef_v_d_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_2D_V_D); + + tmp = (u32)gb_2d->lssc_paracoef_hv_lu_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_2d->lssc_paracoef_hv_lu_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_2D_HV_LU); + + tmp = (u32)gb_2d->lssc_paracoef_hv_ru_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_2d->lssc_paracoef_hv_ru_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_2D_HV_RU); + + tmp = (u32)gb_2d->lssc_paracoef_hv_ld_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_2d->lssc_paracoef_hv_ld_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_2D_HV_LD); + + tmp = (u32)gb_2d->lssc_paracoef_hv_rd_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_2d->lssc_paracoef_hv_rd_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_2D_HV_RD); + + /* GB 4D */ + gb_4d = ¶m->lssc_parabola_param->gb_4d; + tmp = (u32)gb_4d->lssc_paracoef_h_l_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_4d->lssc_paracoef_h_l_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_4D_H_L); + + tmp = (u32)gb_4d->lssc_paracoef_h_r_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_4d->lssc_paracoef_h_r_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_4D_H_R); + + tmp = (u32)gb_4d->lssc_paracoef_v_u_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_4d->lssc_paracoef_v_u_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_4D_V_U); + + tmp = (u32)gb_4d->lssc_paracoef_v_d_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_4d->lssc_paracoef_v_d_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_4D_V_D); + + tmp = (u32)gb_4d->lssc_paracoef_hv_lu_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_4d->lssc_paracoef_hv_lu_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_4D_HV_LU); + + tmp = (u32)gb_4d->lssc_paracoef_hv_ru_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_4d->lssc_paracoef_hv_ru_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_4D_HV_RU); + + tmp = (u32)gb_4d->lssc_paracoef_hv_ld_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_4d->lssc_paracoef_hv_ld_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_4D_HV_LD); + + tmp = (u32)gb_4d->lssc_paracoef_hv_rd_max & 0x1fffU; + val = (tmp << 16U) | (u32)(gb_4d->lssc_paracoef_hv_rd_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_GB_COEF_4D_HV_RD); + + /* B 2D */ + b_2d = ¶m->lssc_parabola_param->b_2d; + tmp = (u32)b_2d->lssc_paracoef_h_l_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_2d->lssc_paracoef_h_l_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_2D_H_L); + + tmp = (u32)b_2d->lssc_paracoef_h_r_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_2d->lssc_paracoef_h_r_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_2D_H_R); + + tmp = (u32)b_2d->lssc_paracoef_v_u_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_2d->lssc_paracoef_v_u_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_2D_V_U); + + tmp = (u32)b_2d->lssc_paracoef_v_d_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_2d->lssc_paracoef_v_d_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_2D_V_D); + + tmp = (u32)b_2d->lssc_paracoef_hv_lu_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_2d->lssc_paracoef_hv_lu_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_2D_HV_LU); + + tmp = (u32)b_2d->lssc_paracoef_hv_ru_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_2d->lssc_paracoef_hv_ru_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_2D_HV_RU); + + tmp = (u32)b_2d->lssc_paracoef_hv_ld_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_2d->lssc_paracoef_hv_ld_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_2D_HV_LD); + + tmp = (u32)b_2d->lssc_paracoef_hv_rd_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_2d->lssc_paracoef_hv_rd_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_2D_HV_RD); + + /* B 4D */ + b_4d = ¶m->lssc_parabola_param->b_4d; + tmp = (u32)b_4d->lssc_paracoef_h_l_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_4d->lssc_paracoef_h_l_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_4D_H_L); + + tmp = (u32)b_4d->lssc_paracoef_h_r_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_4d->lssc_paracoef_h_r_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_4D_H_R); + + tmp = (u32)b_4d->lssc_paracoef_v_u_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_4d->lssc_paracoef_v_u_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_4D_V_U); + + tmp = (u32)b_4d->lssc_paracoef_v_d_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_4d->lssc_paracoef_v_d_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_4D_V_D); + + tmp = (u32)b_4d->lssc_paracoef_hv_lu_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_4d->lssc_paracoef_hv_lu_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_4D_HV_LU); + + tmp = (u32)b_4d->lssc_paracoef_hv_ru_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_4d->lssc_paracoef_hv_ru_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_4D_HV_RU); + + tmp = (u32)b_4d->lssc_paracoef_hv_ld_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_4d->lssc_paracoef_hv_ld_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_4D_HV_LD); + + tmp = (u32)b_4d->lssc_paracoef_hv_rd_max & 0x1fffU; + val = (tmp << 16U) | (u32)(b_4d->lssc_paracoef_hv_rd_min & 0x1fffU); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PARA_B_COEF_4D_HV_RD); + + } else { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_LSSC_PARA_EN); + } + + /* grid shading */ + if (param->lssc_grid_param) { + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_LSSC_GRID_EN); + writel(grid_h_size, &res->capture_reg->l1isp.L1_LSSC_GRID_H_SIZE); + writel(grid_v_size, &res->capture_reg->l1isp.L1_LSSC_GRID_V_SIZE); + writel(param->lssc_grid_param->lssc_grid_h_center, + &res->capture_reg->l1isp.L1_LSSC_GRID_H_CENTER); + writel(param->lssc_grid_param->lssc_grid_v_center, + &res->capture_reg->l1isp.L1_LSSC_GRID_V_CENTER); + writel(param->lssc_grid_param->lssc_grid_mgsel, + &res->capture_reg->l1isp.L1_LSSC_GRID_MGSEL); + + } else { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_LSSC_GRID_EN); + } + + /* preset white balance */ + val = (param->lssc_pwhb_r_gain_max << 16U) | (param->lssc_pwhb_r_gain_min); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PWHB_R_GAIN); + + val = (param->lssc_pwhb_gr_gain_max << 16U) | (param->lssc_pwhb_gr_gain_min); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PWHB_GR_GAIN); + + val = (param->lssc_pwhb_gb_gain_max << 16U) | (param->lssc_pwhb_gb_gain_min); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PWHB_GB_GAIN); + + val = (param->lssc_pwhb_b_gain_max << 16U) | (param->lssc_pwhb_b_gain_min); + writel(val, &res->capture_reg->l1isp.L1_LSSC_PWHB_B_GAIN); + + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_LSSC_EN); + + return 0; +} + +/** + * hwd_viif_l1_set_lsc_table_transmission() - Configure L1ISP transferring lens shading grid table. + * + * @table_gr: grid shading table for Gr(physical address) + * @table_r: grid shading table for R(physical address) + * @table_b: grid shading table for B(physical address) + * @table_gb: grid shading table for Gb(physical address) + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "table_h", "table_m" or "table_l" is not 8byte alignment + * + * Note that when 0 is set to table address, table transfer of the table is disabled. + */ +s32 hwd_viif_l1_set_lsc_table_transmission(struct hwd_viif_res *res, uintptr_t table_gr, + uintptr_t table_r, uintptr_t table_b, uintptr_t table_gb) +{ + u32 val = 0x0U; + + if (((table_gr % HWD_VIIF_L1_VDM_ALIGN) != 0U) || + ((table_r % HWD_VIIF_L1_VDM_ALIGN) != 0U) || + ((table_b % HWD_VIIF_L1_VDM_ALIGN) != 0U) || + ((table_gb % HWD_VIIF_L1_VDM_ALIGN) != 0U)) { + return -EINVAL; + } + /* VDM common settings */ + writel(HWD_VIIF_L1_VDM_CFG_PARAM, &res->capture_reg->vdm.t_group[0].VDM_T_CFG); + writel(HWD_VIIF_L1_VDM_SRAM_BASE, &res->capture_reg->vdm.t_group[0].VDM_T_SRAM_BASE); + writel(HWD_VIIF_L1_VDM_SRAM_SIZE, &res->capture_reg->vdm.t_group[0].VDM_T_SRAM_SIZE); + + if (table_gr != 0U) { + writel((u32)table_gr, &res->capture_reg->vdm.t_port[4].VDM_T_STADR); + writel(HWD_VIIF_L1_VDM_LSC_TABLE_SIZE, &res->capture_reg->vdm.t_port[4].VDM_T_SIZE); + val |= 0x10U; + } + + if (table_r != 0U) { + writel((u32)table_r, &res->capture_reg->vdm.t_port[5].VDM_T_STADR); + writel(HWD_VIIF_L1_VDM_LSC_TABLE_SIZE, &res->capture_reg->vdm.t_port[5].VDM_T_SIZE); + val |= 0x20U; + } + + if (table_b != 0U) { + writel((u32)table_b, &res->capture_reg->vdm.t_port[6].VDM_T_STADR); + writel(HWD_VIIF_L1_VDM_LSC_TABLE_SIZE, &res->capture_reg->vdm.t_port[6].VDM_T_SIZE); + val |= 0x40U; + } + + if (table_gb != 0U) { + writel((u32)table_gb, &res->capture_reg->vdm.t_port[7].VDM_T_STADR); + writel(HWD_VIIF_L1_VDM_LSC_TABLE_SIZE, &res->capture_reg->vdm.t_port[7].VDM_T_SIZE); + val |= 0x80U; + } + + val |= (readl(&res->capture_reg->vdm.VDM_T_ENABLE) & 0xffffff0fU); + writel(val, &res->capture_reg->vdm.VDM_T_ENABLE); + + return 0; +} + +/** + * hwd_viif_l1_set_main_process() - Configure L1ISP main process. + * + * @demosaic_mode: demosaic mode @ref hwd_viif_l1_demosaic + * @damp_lsbsel: output pixel clip range for auto white balance [0..15] + * @color_matrix: pointer to color matrix correction parameters + * @dst_maxval: output pixel maximum value [0x0..0xffffff] + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * main process means digital amp, demosaic, and color matrix correction + * NULL means disabling color matrix correction + * - "demosaic_mode" is neither HWD_VIIF_L1_DEMOSAIC_ACPI nor HWD_VIIF_L1_DEMOSAIC_DMG + * - "damp_lsbsel" is out of range + * - each parameter of "color_matrix" is out of range + * - "dst_maxval" is out of range + */ +s32 hwd_viif_l1_set_main_process(struct hwd_viif_res *res, u32 demosaic_mode, u32 damp_lsbsel, + const struct viif_l1_color_matrix_correction *color_matrix, + u32 dst_maxval) +{ + u32 val; + + if (demosaic_mode != HWD_VIIF_L1_DEMOSAIC_ACPI && + demosaic_mode != HWD_VIIF_L1_DEMOSAIC_DMG) { + return -EINVAL; + } + + if (damp_lsbsel > HWD_VIIF_DAMP_MAX_LSBSEL) + return -EINVAL; + + if (color_matrix) { + if (color_matrix->coef_rmg_min > color_matrix->coef_rmg_max || + color_matrix->coef_rmb_min > color_matrix->coef_rmb_max || + color_matrix->coef_gmr_min > color_matrix->coef_gmr_max || + color_matrix->coef_gmb_min > color_matrix->coef_gmb_max || + color_matrix->coef_bmr_min > color_matrix->coef_bmr_max || + color_matrix->coef_bmg_min > color_matrix->coef_bmg_max || + (u32)color_matrix->dst_minval > dst_maxval) + return -EINVAL; + } + + if (dst_maxval > HWD_VIIF_MAIN_PROCESS_MAX_OUT_PIXEL_VAL) + return -EINVAL; + + val = damp_lsbsel << 4U; + writel(val, &res->capture_reg->l1isp.L1_MPRO_CONF); + + writel(demosaic_mode, &res->capture_reg->l1isp.L1_MPRO_LCS_MODE); + + if (color_matrix) { + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_MPRO_SW); + + val = (u32)color_matrix->coef_rmg_min & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_RMG_MIN); + + val = (u32)color_matrix->coef_rmg_max & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_RMG_MAX); + + val = (u32)color_matrix->coef_rmb_min & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_RMB_MIN); + + val = (u32)color_matrix->coef_rmb_max & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_RMB_MAX); + + val = (u32)color_matrix->coef_gmr_min & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_GMR_MIN); + + val = (u32)color_matrix->coef_gmr_max & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_GMR_MAX); + + val = (u32)color_matrix->coef_gmb_min & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_GMB_MIN); + + val = (u32)color_matrix->coef_gmb_max & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_GMB_MAX); + + val = (u32)color_matrix->coef_bmr_min & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_BMR_MIN); + + val = (u32)color_matrix->coef_bmr_max & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_BMR_MAX); + + val = (u32)color_matrix->coef_bmg_min & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_BMG_MIN); + + val = (u32)color_matrix->coef_bmg_max & 0xffffU; + writel(val, &res->capture_reg->l1isp.L1_MPRO_LM0_BMG_MAX); + + writel((u32)color_matrix->dst_minval, &res->capture_reg->l1isp.L1_MPRO_DST_MINVAL); + } else { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_MPRO_SW); + } + + writel(dst_maxval, &res->capture_reg->l1isp.L1_MPRO_DST_MAXVAL); + + return 0; +} + +/** + * hwd_viif_l1_set_awb() - Configure L1ISP auto white balance parameters. + * + * @param: pointer to auto white balance parameters; NULL means disabling auto white balance + * @awhb_wbmrg: R gain of white balance adjustment [0x40..0x3FF] accuracy: 1/256 + * @awhb_wbmgg: G gain of white balance adjustment [0x40..0x3FF] accuracy: 1/256 + * @awhb_wbmbg: B gain of white balance adjustment [0x40..0x3FF] accuracy: 1/256 + * Return: 0 operation completed successfully + * Return: -EINVAL + * - each parameter of "param" is out of range + * - awhb_wbm*g is out of range + */ +s32 hwd_viif_l1_set_awb(struct hwd_viif_res *res, const struct viif_l1_awb *param, u32 awhb_wbmrg, + u32 awhb_wbmgg, u32 awhb_wbmbg) +{ + u32 val, ygate_data; + + if (awhb_wbmrg < HWD_VIIF_AWB_MIN_GAIN || awhb_wbmrg >= HWD_VIIF_AWB_MAX_GAIN || + awhb_wbmgg < HWD_VIIF_AWB_MIN_GAIN || awhb_wbmgg >= HWD_VIIF_AWB_MAX_GAIN || + awhb_wbmbg < HWD_VIIF_AWB_MIN_GAIN || awhb_wbmbg >= HWD_VIIF_AWB_MAX_GAIN) { + return -EINVAL; + } + + if (param) { + if (param->awhb_ygate_sel != HWD_VIIF_ENABLE && + param->awhb_ygate_sel != HWD_VIIF_DISABLE) { + return -EINVAL; + } + + if (param->awhb_ygate_data != 64U && param->awhb_ygate_data != 128U && + param->awhb_ygate_data != 256U && param->awhb_ygate_data != 512U) { + return -EINVAL; + } + + if (param->awhb_cgrange != HWD_VIIF_L1_AWB_ONE_SECOND && + param->awhb_cgrange != HWD_VIIF_L1_AWB_X1 && + param->awhb_cgrange != HWD_VIIF_L1_AWB_X2 && + param->awhb_cgrange != HWD_VIIF_L1_AWB_X4) { + return -EINVAL; + } + + if (param->awhb_ygatesw != HWD_VIIF_ENABLE && + param->awhb_ygatesw != HWD_VIIF_DISABLE) { + return -EINVAL; + } + + if (param->awhb_hexsw != HWD_VIIF_ENABLE && param->awhb_hexsw != HWD_VIIF_DISABLE) + return -EINVAL; + + if (param->awhb_areamode != HWD_VIIF_L1_AWB_AREA_MODE0 && + param->awhb_areamode != HWD_VIIF_L1_AWB_AREA_MODE1 && + param->awhb_areamode != HWD_VIIF_L1_AWB_AREA_MODE2 && + param->awhb_areamode != HWD_VIIF_L1_AWB_AREA_MODE3) { + return -EINVAL; + } + + val = readl(&res->capture_reg->l1isp.L1_SYSM_WIDTH); + if (param->awhb_area_hsize < 1U || (param->awhb_area_hsize > ((val - 8U) / 8U)) || + param->awhb_area_hofs > (val - 9U)) { + return -EINVAL; + } + + val = readl(&res->capture_reg->l1isp.L1_SYSM_HEIGHT); + if (param->awhb_area_vsize < 1U || (param->awhb_area_vsize > ((val - 4U) / 8U)) || + param->awhb_area_vofs > (val - 5U)) { + return -EINVAL; + } + + if ((param->awhb_sq_sw[0] != HWD_VIIF_ENABLE && + param->awhb_sq_sw[0] != HWD_VIIF_DISABLE) || + (param->awhb_sq_sw[1] != HWD_VIIF_ENABLE && + param->awhb_sq_sw[1] != HWD_VIIF_DISABLE) || + (param->awhb_sq_sw[2] != HWD_VIIF_ENABLE && + param->awhb_sq_sw[2] != HWD_VIIF_DISABLE) || + (param->awhb_sq_pol[0] != HWD_VIIF_ENABLE && + param->awhb_sq_pol[0] != HWD_VIIF_DISABLE) || + (param->awhb_sq_pol[1] != HWD_VIIF_ENABLE && + param->awhb_sq_pol[1] != HWD_VIIF_DISABLE) || + (param->awhb_sq_pol[2] != HWD_VIIF_ENABLE && + param->awhb_sq_pol[2] != HWD_VIIF_DISABLE)) { + return -EINVAL; + } + + if (param->awhb_bycut0p > HWD_VIIF_AWB_UNSIGNED_GATE_UPPER || + param->awhb_bycut0n > HWD_VIIF_AWB_UNSIGNED_GATE_UPPER || + param->awhb_rycut0p > HWD_VIIF_AWB_UNSIGNED_GATE_UPPER || + param->awhb_rycut0n > HWD_VIIF_AWB_UNSIGNED_GATE_UPPER || + param->awhb_rbcut0h < HWD_VIIF_AWB_GATE_LOWER || + param->awhb_rbcut0h > HWD_VIIF_AWB_GATE_UPPER || + param->awhb_rbcut0l < HWD_VIIF_AWB_GATE_LOWER || + param->awhb_rbcut0l > HWD_VIIF_AWB_GATE_UPPER || + param->awhb_bycut_h[0] < HWD_VIIF_AWB_GATE_LOWER || + param->awhb_bycut_h[0] > HWD_VIIF_AWB_GATE_UPPER || + param->awhb_bycut_h[1] < HWD_VIIF_AWB_GATE_LOWER || + param->awhb_bycut_h[1] > HWD_VIIF_AWB_GATE_UPPER || + param->awhb_bycut_h[2] < HWD_VIIF_AWB_GATE_LOWER || + param->awhb_bycut_h[2] > HWD_VIIF_AWB_GATE_UPPER || + param->awhb_bycut_l[0] > HWD_VIIF_AWB_UNSIGNED_GATE_UPPER || + param->awhb_bycut_l[1] > HWD_VIIF_AWB_UNSIGNED_GATE_UPPER || + param->awhb_bycut_l[2] > HWD_VIIF_AWB_UNSIGNED_GATE_UPPER || + param->awhb_rycut_h[0] < HWD_VIIF_AWB_GATE_LOWER || + param->awhb_rycut_h[0] > HWD_VIIF_AWB_GATE_UPPER || + param->awhb_rycut_h[1] < HWD_VIIF_AWB_GATE_LOWER || + param->awhb_rycut_h[1] > HWD_VIIF_AWB_GATE_UPPER || + param->awhb_rycut_h[2] < HWD_VIIF_AWB_GATE_LOWER || + param->awhb_rycut_h[2] > HWD_VIIF_AWB_GATE_UPPER || + param->awhb_rycut_l[0] > HWD_VIIF_AWB_UNSIGNED_GATE_UPPER || + param->awhb_rycut_l[1] > HWD_VIIF_AWB_UNSIGNED_GATE_UPPER || + param->awhb_rycut_l[2] > HWD_VIIF_AWB_UNSIGNED_GATE_UPPER || + param->awhb_awbsftu < HWD_VIIF_AWB_GATE_LOWER || + param->awhb_awbsftu > HWD_VIIF_AWB_GATE_UPPER || + param->awhb_awbsftv < HWD_VIIF_AWB_GATE_LOWER || + param->awhb_awbsftv > HWD_VIIF_AWB_GATE_UPPER || + (param->awhb_awbhuecor != HWD_VIIF_ENABLE && + param->awhb_awbhuecor != HWD_VIIF_DISABLE)) { + return -EINVAL; + } + + if (param->awhb_awbspd > HWD_VIIF_AWB_MAX_UV_CONVERGENCE_SPEED || + param->awhb_awbulv > HWD_VIIF_AWB_MAX_UV_CONVERGENCE_LEVEL || + param->awhb_awbvlv > HWD_VIIF_AWB_MAX_UV_CONVERGENCE_LEVEL || + param->awhb_awbondot > HWD_VIIF_AWB_INTEGRATION_STOP_TH) { + return -EINVAL; + } + + switch (param->awhb_awbfztim) { + case HWD_VIIF_L1_AWB_RESTART_NO: + case HWD_VIIF_L1_AWB_RESTART_128FRAME: + case HWD_VIIF_L1_AWB_RESTART_64FRAME: + case HWD_VIIF_L1_AWB_RESTART_32FRAME: + case HWD_VIIF_L1_AWB_RESTART_16FRAME: + case HWD_VIIF_L1_AWB_RESTART_8FRAME: + case HWD_VIIF_L1_AWB_RESTART_4FRAME: + case HWD_VIIF_L1_AWB_RESTART_2FRAME: + break; + default: + return -EINVAL; + } + } + + writel(awhb_wbmrg, &res->capture_reg->l1isp.L1_AWHB_WBMRG); + writel(awhb_wbmgg, &res->capture_reg->l1isp.L1_AWHB_WBMGG); + writel(awhb_wbmbg, &res->capture_reg->l1isp.L1_AWHB_WBMBG); + + val = readl(&res->capture_reg->l1isp.L1_AWHB_SW) & 0xffffff7fU; + + if (param) { + val |= (HWD_VIIF_ENABLE << 7U); + writel(val, &res->capture_reg->l1isp.L1_AWHB_SW); + + if (param->awhb_ygate_data == 64U) + ygate_data = 0U; + else if (param->awhb_ygate_data == 128U) + ygate_data = 1U; + else if (param->awhb_ygate_data == 256U) + ygate_data = 2U; + else + ygate_data = 3U; + + val = (param->awhb_ygate_sel << 7U) | (ygate_data << 5U) | (param->awhb_cgrange); + writel(val, &res->capture_reg->l1isp.L1_AWHB_GATE_CONF0); + + val = (param->awhb_ygatesw << 5U) | (param->awhb_hexsw << 4U) | + (param->awhb_areamode); + writel(val, &res->capture_reg->l1isp.L1_AWHB_GATE_CONF1); + + writel(param->awhb_area_hsize, &res->capture_reg->l1isp.L1_AWHB_AREA_HSIZE); + writel(param->awhb_area_vsize, &res->capture_reg->l1isp.L1_AWHB_AREA_VSIZE); + writel(param->awhb_area_hofs, &res->capture_reg->l1isp.L1_AWHB_AREA_HOFS); + writel(param->awhb_area_vofs, &res->capture_reg->l1isp.L1_AWHB_AREA_VOFS); + + writel(param->awhb_area_maskh, &res->capture_reg->l1isp.L1_AWHB_AREA_MASKH); + writel(param->awhb_area_maskl, &res->capture_reg->l1isp.L1_AWHB_AREA_MASKL); + + val = (param->awhb_sq_sw[0] << 7U) | (param->awhb_sq_pol[0] << 6U) | + (param->awhb_sq_sw[1] << 5U) | (param->awhb_sq_pol[1] << 4U) | + (param->awhb_sq_sw[2] << 3U) | (param->awhb_sq_pol[2] << 2U); + writel(val, &res->capture_reg->l1isp.L1_AWHB_SQ_CONF); + + writel((u32)param->awhb_ygateh, &res->capture_reg->l1isp.L1_AWHB_YGATEH); + writel((u32)param->awhb_ygatel, &res->capture_reg->l1isp.L1_AWHB_YGATEL); + + writel(param->awhb_bycut0p, &res->capture_reg->l1isp.L1_AWHB_BYCUT0P); + writel(param->awhb_bycut0n, &res->capture_reg->l1isp.L1_AWHB_BYCUT0N); + writel(param->awhb_rycut0p, &res->capture_reg->l1isp.L1_AWHB_RYCUT0P); + writel(param->awhb_rycut0n, &res->capture_reg->l1isp.L1_AWHB_RYCUT0N); + + val = (u32)param->awhb_rbcut0h & 0xffU; + writel(val, &res->capture_reg->l1isp.L1_AWHB_RBCUT0H); + val = (u32)param->awhb_rbcut0l & 0xffU; + writel(val, &res->capture_reg->l1isp.L1_AWHB_RBCUT0L); + + val = (u32)param->awhb_bycut_h[0] & 0xffU; + writel(val, &res->capture_reg->l1isp.L1_AWHB_BYCUT1H); + writel(param->awhb_bycut_l[0], &res->capture_reg->l1isp.L1_AWHB_BYCUT1L); + val = (u32)param->awhb_bycut_h[1] & 0xffU; + writel(val, &res->capture_reg->l1isp.L1_AWHB_BYCUT2H); + writel(param->awhb_bycut_l[1], &res->capture_reg->l1isp.L1_AWHB_BYCUT2L); + val = (u32)param->awhb_bycut_h[2] & 0xffU; + writel(val, &res->capture_reg->l1isp.L1_AWHB_BYCUT3H); + writel(param->awhb_bycut_l[2], &res->capture_reg->l1isp.L1_AWHB_BYCUT3L); + + val = (u32)param->awhb_rycut_h[0] & 0xffU; + writel(val, &res->capture_reg->l1isp.L1_AWHB_RYCUT1H); + writel(param->awhb_rycut_l[0], &res->capture_reg->l1isp.L1_AWHB_RYCUT1L); + val = (u32)param->awhb_rycut_h[1] & 0xffU; + writel(val, &res->capture_reg->l1isp.L1_AWHB_RYCUT2H); + writel(param->awhb_rycut_l[1], &res->capture_reg->l1isp.L1_AWHB_RYCUT2L); + val = (u32)param->awhb_rycut_h[2] & 0xffU; + writel(val, &res->capture_reg->l1isp.L1_AWHB_RYCUT3H); + writel(param->awhb_rycut_l[2], &res->capture_reg->l1isp.L1_AWHB_RYCUT3L); + + val = (u32)param->awhb_awbsftu & 0xffU; + writel(val, &res->capture_reg->l1isp.L1_AWHB_AWBSFTU); + val = (u32)param->awhb_awbsftv & 0xffU; + writel(val, &res->capture_reg->l1isp.L1_AWHB_AWBSFTV); + + val = (param->awhb_awbhuecor << 4U) | (param->awhb_awbspd); + writel(val, &res->capture_reg->l1isp.L1_AWHB_AWBSPD); + + writel(param->awhb_awbulv, &res->capture_reg->l1isp.L1_AWHB_AWBULV); + writel(param->awhb_awbvlv, &res->capture_reg->l1isp.L1_AWHB_AWBVLV); + writel((u32)param->awhb_awbwait, &res->capture_reg->l1isp.L1_AWHB_AWBWAIT); + + writel(param->awhb_awbondot, &res->capture_reg->l1isp.L1_AWHB_AWBONDOT); + writel(param->awhb_awbfztim, &res->capture_reg->l1isp.L1_AWHB_AWBFZTIM); + + writel((u32)param->awhb_wbgrmax, &res->capture_reg->l1isp.L1_AWHB_WBGRMAX); + writel((u32)param->awhb_wbgbmax, &res->capture_reg->l1isp.L1_AWHB_WBGBMAX); + writel((u32)param->awhb_wbgrmin, &res->capture_reg->l1isp.L1_AWHB_WBGRMIN); + writel((u32)param->awhb_wbgbmin, &res->capture_reg->l1isp.L1_AWHB_WBGBMIN); + + } else { + /* disable awb */ + writel(val, &res->capture_reg->l1isp.L1_AWHB_SW); + } + + return 0; +} + +/** + * hwd_viif_l1_lock_awb_gain() - Configure L1ISP lock auto white balance gain. + * + * @enable: enable/disable lock AWB gain + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - "enable" is neither HWD_VIIF_ENABLE nor HWD_VIIF_DISABLE + */ +s32 hwd_viif_l1_lock_awb_gain(struct hwd_viif_res *res, u32 enable) +{ + u32 val; + + if (enable != HWD_VIIF_ENABLE && enable != HWD_VIIF_DISABLE) + return -EINVAL; + + val = readl(&res->capture_reg->l1isp.L1_AWHB_SW) & 0xffffffdfU; + val |= (enable << 5U); + writel(val, &res->capture_reg->l1isp.L1_AWHB_SW); + + return 0; +} + +/** + * hwd_viif_l1_set_hdrc() - Configure L1ISP HDR compression parameters. + * + * @param: pointer to HDR compression parameters + * @hdrc_thr_sft_amt: shift value in case of through mode [0..8] + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - each parameter of "param" is out of range + * - hdrc_thr_sft_amt is out of range when param is NULL + * - hdrc_thr_sft_amt is not 0 when param is not NULL + */ +s32 hwd_viif_l1_set_hdrc(struct hwd_viif_res *res, const struct viif_l1_hdrc *param, + u32 hdrc_thr_sft_amt) +{ + u32 val, sw_delay1; + + if (!param) { + if (hdrc_thr_sft_amt > HWD_VIIF_L1_HDRC_MAX_THROUGH_SHIFT_VAL) + return -EINVAL; + + writel(hdrc_thr_sft_amt, &res->capture_reg->l1isp.L1_HDRC_THR_SFT_AMT); + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_HDRC_EN); + + return 0; + } + + if (hdrc_thr_sft_amt != 0U || param->hdrc_ratio < HWD_VIIF_L1_HDRC_MIN_INPUT_DATA_WIDTH || + param->hdrc_ratio > HWD_VIIF_L1_HDRC_MAX_INPUT_DATA_WIDTH || + param->hdrc_pt_ratio > HWD_VIIF_L1_HDRC_MAX_PT_SLOPE || + param->hdrc_pt_blend > HWD_VIIF_L1_HDRC_MAX_BLEND_RATIO || + param->hdrc_pt_blend2 > HWD_VIIF_L1_HDRC_MAX_BLEND_RATIO || + (param->hdrc_pt_blend + param->hdrc_pt_blend2) > HWD_VIIF_L1_HDRC_MAX_BLEND_RATIO || + (param->hdrc_tn_type != HWD_VIIF_L1_HDRC_TONE_USER && + param->hdrc_tn_type != HWD_VIIF_L1_HDRC_TONE_PRESET) || + param->hdrc_flr_val > HWD_VIIF_L1_HDRC_MAX_FLARE_VAL || + (param->hdrc_flr_adp != HWD_VIIF_ENABLE && param->hdrc_flr_adp != HWD_VIIF_DISABLE) || + (param->hdrc_ybr_off != HWD_VIIF_ENABLE && param->hdrc_ybr_off != HWD_VIIF_DISABLE) || + param->hdrc_orgy_blend > HWD_VIIF_L1_HDRC_MAX_BLEND_LUMA) { + return -EINVAL; + } + + writel((param->hdrc_ratio - HWD_VIIF_L1_HDRC_RATIO_OFFSET), + &res->capture_reg->l1isp.L1_HDRC_RATIO); + writel(param->hdrc_pt_ratio, &res->capture_reg->l1isp.L1_HDRC_PT_RATIO); + + writel(param->hdrc_pt_blend, &res->capture_reg->l1isp.L1_HDRC_PT_BLEND); + writel(param->hdrc_pt_blend2, &res->capture_reg->l1isp.L1_HDRC_PT_BLEND2); + + writel(param->hdrc_pt_sat, &res->capture_reg->l1isp.L1_HDRC_PT_SAT); + writel(param->hdrc_tn_type, &res->capture_reg->l1isp.L1_HDRC_TN_TYPE); + + writel(param->hdrc_utn_tbl[0], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL0); + writel(param->hdrc_utn_tbl[1], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL1); + writel(param->hdrc_utn_tbl[2], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL2); + writel(param->hdrc_utn_tbl[3], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL3); + writel(param->hdrc_utn_tbl[4], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL4); + writel(param->hdrc_utn_tbl[5], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL5); + writel(param->hdrc_utn_tbl[6], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL6); + writel(param->hdrc_utn_tbl[7], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL7); + writel(param->hdrc_utn_tbl[8], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL8); + writel(param->hdrc_utn_tbl[9], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL9); + writel(param->hdrc_utn_tbl[10], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL10); + writel(param->hdrc_utn_tbl[11], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL11); + writel(param->hdrc_utn_tbl[12], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL12); + writel(param->hdrc_utn_tbl[13], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL13); + writel(param->hdrc_utn_tbl[14], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL14); + writel(param->hdrc_utn_tbl[15], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL15); + writel(param->hdrc_utn_tbl[16], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL16); + writel(param->hdrc_utn_tbl[17], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL17); + writel(param->hdrc_utn_tbl[18], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL18); + writel(param->hdrc_utn_tbl[19], &res->capture_reg->l1isp.L1_HDRC_UTN_TBL19); + + writel(param->hdrc_flr_val, &res->capture_reg->l1isp.L1_HDRC_FLR_VAL); + writel(param->hdrc_flr_adp, &res->capture_reg->l1isp.L1_HDRC_FLR_ADP); + + writel(param->hdrc_ybr_off, &res->capture_reg->l1isp.L1_HDRC_YBR_OFF); + writel(param->hdrc_orgy_blend, &res->capture_reg->l1isp.L1_HDRC_ORGY_BLEND); + + val = ((readl(&res->capture_reg->l1isp.L1_SYSM_HEIGHT)) % 64U) / 2U; + writel(val, &res->capture_reg->l1isp.L1_HDRC_MAR_TOP); + val = ((readl(&res->capture_reg->l1isp.L1_SYSM_WIDTH)) % 64U) / 2U; + writel(val, &res->capture_reg->l1isp.L1_HDRC_MAR_LEFT); + + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_HDRC_EN); + + /* update of sw_delay1 must be done when MAIN unit is NOT running. */ + if (!res->run_flag_main) { + sw_delay1 = (u32)((HWD_VIIF_REGBUF_ACCESS_TIME * (u64)res->pixel_clock) / + ((u64)res->htotal_size * HWD_VIIF_SYS_CLK)) + + HWD_VIIF_L1_DELAY_W_HDRC + 1U; + val = readl(&res->capture_reg->sys.INT_M1_LINE) & 0xffffU; + val |= (sw_delay1 << 16U); + writel(val, &res->capture_reg->sys.INT_M1_LINE); + /* M2_LINE is the same condition as M1_LINE */ + writel(val, &res->capture_reg->sys.INT_M2_LINE); + } + + return 0; +} + +/** + * hwd_viif_l1_set_hdrc_ltm() - Configure L1ISP HDR compression local tone mapping parameters. + * + * @param: pointer to HDR compression local tone mapping parameters + * Return: 0 operation completed successfully + * Return: -EINVAL + * - "param" is NULL + * - each parameter of "param" is out of range + */ +s32 hwd_viif_l1_set_hdrc_ltm(struct hwd_viif_res *res, const struct viif_l1_hdrc_ltm_config *param) +{ + u32 val; + u32 idx; + + if (!param || param->tnp_max >= HWD_VIIF_L1_HDRC_MAX_LTM_TONE_BLEND_RATIO || + param->tnp_mag >= HWD_VIIF_L1_HDRC_MAX_LTM_MAGNIFICATION) { + return -EINVAL; + } + + val = (u32)param->tnp_fil[0]; + for (idx = 1; idx < 5U; idx++) + val += (u32)param->tnp_fil[idx] * 2U; + + if (val != 1024U) + return -EINVAL; + + writel(param->tnp_max, &res->capture_reg->l1isp.L1_HDRC_TNP_MAX); + + writel(param->tnp_mag, &res->capture_reg->l1isp.L1_HDRC_TNP_MAG); + + writel((u32)param->tnp_fil[0], &res->capture_reg->l1isp.L1_HDRC_TNP_FIL0); + writel((u32)param->tnp_fil[1], &res->capture_reg->l1isp.L1_HDRC_TNP_FIL1); + writel((u32)param->tnp_fil[2], &res->capture_reg->l1isp.L1_HDRC_TNP_FIL2); + writel((u32)param->tnp_fil[3], &res->capture_reg->l1isp.L1_HDRC_TNP_FIL3); + writel((u32)param->tnp_fil[4], &res->capture_reg->l1isp.L1_HDRC_TNP_FIL4); + + return 0; +} + +/** + * hwd_viif_l1_set_gamma() - Configure L1ISP gamma correction parameters. + * + * @param: pointer to gamma correction parameters + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - each parameter of "param" is out of range + */ +s32 hwd_viif_l1_set_gamma(struct hwd_viif_res *res, const struct viif_l1_gamma *param) +{ + u32 idx; + + if (!param) { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_PGC_SW); + return 0; + } + + for (idx = 0; idx < 44U; idx++) { + if (param->gam_p[idx] > HWD_VIIF_L1_GAMMA_MAX_VAL) + return -EINVAL; + } + + writel(param->gam_p[0], &res->capture_reg->l1isp.L1_VPRO_GAM01P); + writel(param->gam_p[1], &res->capture_reg->l1isp.L1_VPRO_GAM02P); + writel(param->gam_p[2], &res->capture_reg->l1isp.L1_VPRO_GAM03P); + writel(param->gam_p[3], &res->capture_reg->l1isp.L1_VPRO_GAM04P); + writel(param->gam_p[4], &res->capture_reg->l1isp.L1_VPRO_GAM05P); + writel(param->gam_p[5], &res->capture_reg->l1isp.L1_VPRO_GAM06P); + writel(param->gam_p[6], &res->capture_reg->l1isp.L1_VPRO_GAM07P); + writel(param->gam_p[7], &res->capture_reg->l1isp.L1_VPRO_GAM08P); + writel(param->gam_p[8], &res->capture_reg->l1isp.L1_VPRO_GAM09P); + writel(param->gam_p[9], &res->capture_reg->l1isp.L1_VPRO_GAM10P); + writel(param->gam_p[10], &res->capture_reg->l1isp.L1_VPRO_GAM11P); + writel(param->gam_p[11], &res->capture_reg->l1isp.L1_VPRO_GAM12P); + writel(param->gam_p[12], &res->capture_reg->l1isp.L1_VPRO_GAM13P); + writel(param->gam_p[13], &res->capture_reg->l1isp.L1_VPRO_GAM14P); + writel(param->gam_p[14], &res->capture_reg->l1isp.L1_VPRO_GAM15P); + writel(param->gam_p[15], &res->capture_reg->l1isp.L1_VPRO_GAM16P); + writel(param->gam_p[16], &res->capture_reg->l1isp.L1_VPRO_GAM17P); + writel(param->gam_p[17], &res->capture_reg->l1isp.L1_VPRO_GAM18P); + writel(param->gam_p[18], &res->capture_reg->l1isp.L1_VPRO_GAM19P); + writel(param->gam_p[19], &res->capture_reg->l1isp.L1_VPRO_GAM20P); + writel(param->gam_p[20], &res->capture_reg->l1isp.L1_VPRO_GAM21P); + writel(param->gam_p[21], &res->capture_reg->l1isp.L1_VPRO_GAM22P); + writel(param->gam_p[22], &res->capture_reg->l1isp.L1_VPRO_GAM23P); + writel(param->gam_p[23], &res->capture_reg->l1isp.L1_VPRO_GAM24P); + writel(param->gam_p[24], &res->capture_reg->l1isp.L1_VPRO_GAM25P); + writel(param->gam_p[25], &res->capture_reg->l1isp.L1_VPRO_GAM26P); + writel(param->gam_p[26], &res->capture_reg->l1isp.L1_VPRO_GAM27P); + writel(param->gam_p[27], &res->capture_reg->l1isp.L1_VPRO_GAM28P); + writel(param->gam_p[28], &res->capture_reg->l1isp.L1_VPRO_GAM29P); + writel(param->gam_p[29], &res->capture_reg->l1isp.L1_VPRO_GAM30P); + writel(param->gam_p[30], &res->capture_reg->l1isp.L1_VPRO_GAM31P); + writel(param->gam_p[31], &res->capture_reg->l1isp.L1_VPRO_GAM32P); + writel(param->gam_p[32], &res->capture_reg->l1isp.L1_VPRO_GAM33P); + writel(param->gam_p[33], &res->capture_reg->l1isp.L1_VPRO_GAM34P); + writel(param->gam_p[34], &res->capture_reg->l1isp.L1_VPRO_GAM35P); + writel(param->gam_p[35], &res->capture_reg->l1isp.L1_VPRO_GAM36P); + writel(param->gam_p[36], &res->capture_reg->l1isp.L1_VPRO_GAM37P); + writel(param->gam_p[37], &res->capture_reg->l1isp.L1_VPRO_GAM38P); + writel(param->gam_p[38], &res->capture_reg->l1isp.L1_VPRO_GAM39P); + writel(param->gam_p[39], &res->capture_reg->l1isp.L1_VPRO_GAM40P); + writel(param->gam_p[40], &res->capture_reg->l1isp.L1_VPRO_GAM41P); + writel(param->gam_p[41], &res->capture_reg->l1isp.L1_VPRO_GAM42P); + writel(param->gam_p[42], &res->capture_reg->l1isp.L1_VPRO_GAM43P); + writel(param->gam_p[43], &res->capture_reg->l1isp.L1_VPRO_GAM44P); + writel(param->blkadj, &res->capture_reg->l1isp.L1_VPRO_BLKADJ); + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_VPRO_PGC_SW); + + return 0; +} + +/** + * hwd_viif_l1_set_img_quality_adjustment() - Configure L1ISP image quality adjustment. + * + * @param: pointer to image quality adjustment parameters; NULL means disabling + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - each parameter of "param" is out of range + */ +s32 hwd_viif_l1_set_img_quality_adjustment(struct hwd_viif_res *res, + const struct hwd_viif_l1_img_quality_adjustment *param) +{ + u32 val; + + if (!param) { + /* disable all features when param is absent */ + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_YUVC_SW); + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_BRIGHT_SW); + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_LCNT_SW); + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_NLCNT_SW); + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_YNR_SW); + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_ETE_SW); + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_CSUP_UVSUP_SW); + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_CSUP_CORING_SW); + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_EDGE_SUP_SW); + writel(1024U, &res->capture_reg->l1isp.L1_VPRO_CB_GAIN); + writel(1024U, &res->capture_reg->l1isp.L1_VPRO_CR_GAIN); + writel(1024U, &res->capture_reg->l1isp.L1_VPRO_CBR_MGAIN_MIN); + writel(0U, &res->capture_reg->l1isp.L1_VPRO_CB_P_GAIN_MAX); + writel(0U, &res->capture_reg->l1isp.L1_VPRO_CB_M_GAIN_MAX); + writel(0U, &res->capture_reg->l1isp.L1_VPRO_CR_P_GAIN_MAX); + writel(0U, &res->capture_reg->l1isp.L1_VPRO_CR_M_GAIN_MAX); + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_CNR_SW); + + return 0; + } + + if (param->lum_noise_reduction) { + if (param->lum_noise_reduction->gain_min > param->lum_noise_reduction->gain_max || + param->lum_noise_reduction->lim_min > param->lum_noise_reduction->lim_max) { + return -EINVAL; + } + } + + if (param->edge_enhancement) { + if (param->edge_enhancement->gain_min > param->edge_enhancement->gain_max || + param->edge_enhancement->lim_min > param->edge_enhancement->lim_max || + param->edge_enhancement->coring_min > param->edge_enhancement->coring_max) { + return -EINVAL; + } + } + + if (param->uv_suppression) { + if (param->uv_suppression->bk_mp >= HWD_VIIF_L1_SUPPRESSION_MAX_VAL || + param->uv_suppression->black >= HWD_VIIF_L1_SUPPRESSION_MAX_VAL || + param->uv_suppression->wh_mp >= HWD_VIIF_L1_SUPPRESSION_MAX_VAL || + param->uv_suppression->white >= HWD_VIIF_L1_SUPPRESSION_MAX_VAL || + param->uv_suppression->bk_slv >= param->uv_suppression->wh_slv) + return -EINVAL; + } + + if (param->coring_suppression) { + if (param->coring_suppression->gain_min > param->coring_suppression->gain_max || + param->coring_suppression->lv_min > param->coring_suppression->lv_max) + return -EINVAL; + } + + if (param->edge_suppression) { + if (param->edge_suppression->lim > HWD_VIIF_L1_EDGE_SUPPRESSION_MAX_LIMIT) + return -EINVAL; + } + + if (param->color_level) { + if (param->color_level->cb_gain >= HWD_VIIF_L1_COLOR_LEVEL_MAX_GAIN || + param->color_level->cr_gain >= HWD_VIIF_L1_COLOR_LEVEL_MAX_GAIN || + param->color_level->cbr_mgain_min >= HWD_VIIF_L1_COLOR_LEVEL_MAX_GAIN || + param->color_level->cbp_gain_max >= HWD_VIIF_L1_COLOR_LEVEL_MAX_GAIN || + param->color_level->cbm_gain_max >= HWD_VIIF_L1_COLOR_LEVEL_MAX_GAIN || + param->color_level->crp_gain_max >= HWD_VIIF_L1_COLOR_LEVEL_MAX_GAIN || + param->color_level->crm_gain_max >= HWD_VIIF_L1_COLOR_LEVEL_MAX_GAIN) { + return -EINVAL; + } + } + + if (param->color_noise_reduction_enable != HWD_VIIF_ENABLE && + param->color_noise_reduction_enable != HWD_VIIF_DISABLE) { + return -EINVAL; + } + + /* RGB to YUV */ + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_VPRO_YUVC_SW); + writel((u32)param->coef_cb, &res->capture_reg->l1isp.L1_VPRO_CB_MAT); + writel((u32)param->coef_cr, &res->capture_reg->l1isp.L1_VPRO_CR_MAT); + + /* brightness */ + val = (u32)param->brightness & 0xffffU; + if (val != 0U) { + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_VPRO_BRIGHT_SW); + writel(val, &res->capture_reg->l1isp.L1_VPRO_BRIGHT); + } else { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_BRIGHT_SW); + } + + /* linear contrast */ + if ((u32)param->linear_contrast != 128U) { + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_VPRO_LCNT_SW); + writel((u32)param->linear_contrast, &res->capture_reg->l1isp.L1_VPRO_LCONT_LEV); + } else { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_LCNT_SW); + } + + /* nonlinear contrast */ + if (param->nonlinear_contrast) { + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_VPRO_NLCNT_SW); + writel((u32)param->nonlinear_contrast->blk_knee, + &res->capture_reg->l1isp.L1_VPRO_BLK_KNEE); + writel((u32)param->nonlinear_contrast->wht_knee, + &res->capture_reg->l1isp.L1_VPRO_WHT_KNEE); + + writel((u32)param->nonlinear_contrast->blk_cont[0], + &res->capture_reg->l1isp.L1_VPRO_BLK_CONT0); + writel((u32)param->nonlinear_contrast->blk_cont[1], + &res->capture_reg->l1isp.L1_VPRO_BLK_CONT1); + writel((u32)param->nonlinear_contrast->blk_cont[2], + &res->capture_reg->l1isp.L1_VPRO_BLK_CONT2); + + writel((u32)param->nonlinear_contrast->wht_cont[0], + &res->capture_reg->l1isp.L1_VPRO_WHT_CONT0); + writel((u32)param->nonlinear_contrast->wht_cont[1], + &res->capture_reg->l1isp.L1_VPRO_WHT_CONT1); + writel((u32)param->nonlinear_contrast->wht_cont[2], + &res->capture_reg->l1isp.L1_VPRO_WHT_CONT2); + } else { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_NLCNT_SW); + } + + /* luminance noise reduction */ + if (param->lum_noise_reduction) { + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_VPRO_YNR_SW); + writel((u32)param->lum_noise_reduction->gain_min, + &res->capture_reg->l1isp.L1_VPRO_YNR_GAIN_MIN); + writel((u32)param->lum_noise_reduction->gain_max, + &res->capture_reg->l1isp.L1_VPRO_YNR_GAIN_MAX); + writel((u32)param->lum_noise_reduction->lim_min, + &res->capture_reg->l1isp.L1_VPRO_YNR_LIM_MIN); + writel((u32)param->lum_noise_reduction->lim_max, + &res->capture_reg->l1isp.L1_VPRO_YNR_LIM_MAX); + } else { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_YNR_SW); + } + + /* edge enhancement */ + if (param->edge_enhancement) { + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_VPRO_ETE_SW); + writel((u32)param->edge_enhancement->gain_min, + &res->capture_reg->l1isp.L1_VPRO_ETE_GAIN_MIN); + writel((u32)param->edge_enhancement->gain_max, + &res->capture_reg->l1isp.L1_VPRO_ETE_GAIN_MAX); + writel((u32)param->edge_enhancement->lim_min, + &res->capture_reg->l1isp.L1_VPRO_ETE_LIM_MIN); + writel((u32)param->edge_enhancement->lim_max, + &res->capture_reg->l1isp.L1_VPRO_ETE_LIM_MAX); + writel((u32)param->edge_enhancement->coring_min, + &res->capture_reg->l1isp.L1_VPRO_ETE_CORING_MIN); + writel((u32)param->edge_enhancement->coring_max, + &res->capture_reg->l1isp.L1_VPRO_ETE_CORING_MAX); + } else { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_ETE_SW); + } + + /* UV suppression */ + if (param->uv_suppression) { + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_VPRO_CSUP_UVSUP_SW); + writel((u32)param->uv_suppression->bk_slv, + &res->capture_reg->l1isp.L1_VPRO_CSUP_BK_SLV); + writel(param->uv_suppression->bk_mp, &res->capture_reg->l1isp.L1_VPRO_CSUP_BK_MP); + writel(param->uv_suppression->black, &res->capture_reg->l1isp.L1_VPRO_CSUP_BLACK); + + writel((u32)param->uv_suppression->wh_slv, + &res->capture_reg->l1isp.L1_VPRO_CSUP_WH_SLV); + writel(param->uv_suppression->wh_mp, &res->capture_reg->l1isp.L1_VPRO_CSUP_WH_MP); + writel(param->uv_suppression->white, &res->capture_reg->l1isp.L1_VPRO_CSUP_WHITE); + } else { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_CSUP_UVSUP_SW); + } + + /* coring suppression */ + if (param->coring_suppression) { + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_VPRO_CSUP_CORING_SW); + writel((u32)param->coring_suppression->lv_min, + &res->capture_reg->l1isp.L1_VPRO_CSUP_CORING_LV_MIN); + writel((u32)param->coring_suppression->lv_max, + &res->capture_reg->l1isp.L1_VPRO_CSUP_CORING_LV_MAX); + writel((u32)param->coring_suppression->gain_min, + &res->capture_reg->l1isp.L1_VPRO_CSUP_CORING_GAIN_MIN); + writel((u32)param->coring_suppression->gain_max, + &res->capture_reg->l1isp.L1_VPRO_CSUP_CORING_GAIN_MAX); + } else { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_CSUP_CORING_SW); + } + + /* edge suppression */ + if (param->edge_suppression) { + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_VPRO_EDGE_SUP_SW); + writel((u32)param->edge_suppression->gain, + &res->capture_reg->l1isp.L1_VPRO_EDGE_SUP_GAIN); + writel((u32)param->edge_suppression->lim, + &res->capture_reg->l1isp.L1_VPRO_EDGE_SUP_LIM); + } else { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_VPRO_EDGE_SUP_SW); + } + + /* color level */ + if (param->color_level) { + writel(param->color_level->cb_gain, &res->capture_reg->l1isp.L1_VPRO_CB_GAIN); + writel(param->color_level->cr_gain, &res->capture_reg->l1isp.L1_VPRO_CR_GAIN); + writel(param->color_level->cbr_mgain_min, + &res->capture_reg->l1isp.L1_VPRO_CBR_MGAIN_MIN); + writel(param->color_level->cbp_gain_max, + &res->capture_reg->l1isp.L1_VPRO_CB_P_GAIN_MAX); + writel(param->color_level->cbm_gain_max, + &res->capture_reg->l1isp.L1_VPRO_CB_M_GAIN_MAX); + writel(param->color_level->crp_gain_max, + &res->capture_reg->l1isp.L1_VPRO_CR_P_GAIN_MAX); + writel(param->color_level->crm_gain_max, + &res->capture_reg->l1isp.L1_VPRO_CR_M_GAIN_MAX); + } else { + /* disable */ + writel(1024U, &res->capture_reg->l1isp.L1_VPRO_CB_GAIN); + writel(1024U, &res->capture_reg->l1isp.L1_VPRO_CR_GAIN); + writel(1024U, &res->capture_reg->l1isp.L1_VPRO_CBR_MGAIN_MIN); + writel(0U, &res->capture_reg->l1isp.L1_VPRO_CB_P_GAIN_MAX); + writel(0U, &res->capture_reg->l1isp.L1_VPRO_CB_M_GAIN_MAX); + writel(0U, &res->capture_reg->l1isp.L1_VPRO_CR_P_GAIN_MAX); + writel(0U, &res->capture_reg->l1isp.L1_VPRO_CR_M_GAIN_MAX); + } + + /* color noise reduction */ + writel(param->color_noise_reduction_enable, &res->capture_reg->l1isp.L1_VPRO_CNR_SW); + + return 0; +} + +/** + * hwd_viif_l1_set_avg_lum_generation() - Configure L1ISP average luminance generation parameters. + * + * @param: pointer to auto exposure parameters + * Return: 0 operation completed successfully + * Return: -EINVAL Parameter error + * - each parameter of "param" is out of range + */ +s32 hwd_viif_l1_set_avg_lum_generation(struct hwd_viif_res *res, + const struct viif_l1_avg_lum_generation_config *param) +{ + u32 idx, j; + u32 val; + + if (!param) { + writel(HWD_VIIF_DISABLE, &res->capture_reg->l1isp.L1_AEXP_ON); + return 0; + } + + val = readl(&res->capture_reg->l1isp.L1_SYSM_WIDTH); + if (param->aexp_start_x > (val - 1U)) + return -EINVAL; + + if (param->aexp_block_width < HWD_VIIF_L1_AEXP_MIN_BLOCK_WIDTH || + param->aexp_block_width > val) { + return -EINVAL; + } + if (param->aexp_block_width % 64U) + return -EINVAL; + + val = readl(&res->capture_reg->l1isp.L1_SYSM_HEIGHT); + if (param->aexp_start_y > (val - 1U)) + return -EINVAL; + + if (param->aexp_block_height < HWD_VIIF_L1_AEXP_MIN_BLOCK_HEIGHT || + param->aexp_block_height > val) { + return -EINVAL; + } + if (param->aexp_block_height % 64U) + return -EINVAL; + + for (idx = 0; idx < 8U; idx++) { + for (j = 0; j < 8U; j++) { + if (param->aexp_weight[idx][j] > HWD_VIIF_L1_AEXP_MAX_WEIGHT) + return -EINVAL; + } + } + + if (param->aexp_satur_ratio > HWD_VIIF_L1_AEXP_MAX_BLOCK_TH || + param->aexp_black_ratio > HWD_VIIF_L1_AEXP_MAX_BLOCK_TH || + param->aexp_satur_level > HWD_VIIF_L1_AEXP_MAX_SATURATION_PIXEL_TH) { + return -EINVAL; + } + + for (idx = 0; idx < 4U; idx++) { + if (param->aexp_ave4linesy[idx] > (val - 4U)) + return -EINVAL; + } + + writel(HWD_VIIF_ENABLE, &res->capture_reg->l1isp.L1_AEXP_ON); + writel(param->aexp_start_x, &res->capture_reg->l1isp.L1_AEXP_START_X); + writel(param->aexp_start_y, &res->capture_reg->l1isp.L1_AEXP_START_Y); + writel(param->aexp_block_width, &res->capture_reg->l1isp.L1_AEXP_BLOCK_WIDTH); + writel(param->aexp_block_height, &res->capture_reg->l1isp.L1_AEXP_BLOCK_HEIGHT); + + val = (param->aexp_weight[0][0] << 14U) | (param->aexp_weight[0][1] << 12U) | + (param->aexp_weight[0][2] << 10U) | (param->aexp_weight[0][3] << 8U) | + (param->aexp_weight[0][4] << 6U) | (param->aexp_weight[0][5] << 4U) | + (param->aexp_weight[0][6] << 2U) | (param->aexp_weight[0][7]); + writel(val, &res->capture_reg->l1isp.L1_AEXP_WEIGHT_0); + + val = (param->aexp_weight[1][0] << 14U) | (param->aexp_weight[1][1] << 12U) | + (param->aexp_weight[1][2] << 10U) | (param->aexp_weight[1][3] << 8U) | + (param->aexp_weight[1][4] << 6U) | (param->aexp_weight[1][5] << 4U) | + (param->aexp_weight[1][6] << 2U) | (param->aexp_weight[1][7]); + writel(val, &res->capture_reg->l1isp.L1_AEXP_WEIGHT_1); + + val = (param->aexp_weight[2][0] << 14U) | (param->aexp_weight[2][1] << 12U) | + (param->aexp_weight[2][2] << 10U) | (param->aexp_weight[2][3] << 8U) | + (param->aexp_weight[2][4] << 6U) | (param->aexp_weight[2][5] << 4U) | + (param->aexp_weight[2][6] << 2U) | (param->aexp_weight[2][7]); + writel(val, &res->capture_reg->l1isp.L1_AEXP_WEIGHT_2); + + val = (param->aexp_weight[3][0] << 14U) | (param->aexp_weight[3][1] << 12U) | + (param->aexp_weight[3][2] << 10U) | (param->aexp_weight[3][3] << 8U) | + (param->aexp_weight[3][4] << 6U) | (param->aexp_weight[3][5] << 4U) | + (param->aexp_weight[3][6] << 2U) | (param->aexp_weight[3][7]); + writel(val, &res->capture_reg->l1isp.L1_AEXP_WEIGHT_3); + + val = (param->aexp_weight[4][0] << 14U) | (param->aexp_weight[4][1] << 12U) | + (param->aexp_weight[4][2] << 10U) | (param->aexp_weight[4][3] << 8U) | + (param->aexp_weight[4][4] << 6U) | (param->aexp_weight[4][5] << 4U) | + (param->aexp_weight[4][6] << 2U) | (param->aexp_weight[4][7]); + writel(val, &res->capture_reg->l1isp.L1_AEXP_WEIGHT_4); + + val = (param->aexp_weight[5][0] << 14U) | (param->aexp_weight[5][1] << 12U) | + (param->aexp_weight[5][2] << 10U) | (param->aexp_weight[5][3] << 8U) | + (param->aexp_weight[5][4] << 6U) | (param->aexp_weight[5][5] << 4U) | + (param->aexp_weight[5][6] << 2U) | (param->aexp_weight[5][7]); + writel(val, &res->capture_reg->l1isp.L1_AEXP_WEIGHT_5); + + val = (param->aexp_weight[6][0] << 14U) | (param->aexp_weight[6][1] << 12U) | + (param->aexp_weight[6][2] << 10U) | (param->aexp_weight[6][3] << 8U) | + (param->aexp_weight[6][4] << 6U) | (param->aexp_weight[6][5] << 4U) | + (param->aexp_weight[6][6] << 2U) | (param->aexp_weight[6][7]); + writel(val, &res->capture_reg->l1isp.L1_AEXP_WEIGHT_6); + + val = (param->aexp_weight[7][0] << 14U) | (param->aexp_weight[7][1] << 12U) | + (param->aexp_weight[7][2] << 10U) | (param->aexp_weight[7][3] << 8U) | + (param->aexp_weight[7][4] << 6U) | (param->aexp_weight[7][5] << 4U) | + (param->aexp_weight[7][6] << 2U) | (param->aexp_weight[7][7]); + writel(val, &res->capture_reg->l1isp.L1_AEXP_WEIGHT_7); + + writel(param->aexp_satur_ratio, &res->capture_reg->l1isp.L1_AEXP_SATUR_RATIO); + writel(param->aexp_black_ratio, &res->capture_reg->l1isp.L1_AEXP_BLACK_RATIO); + writel(param->aexp_satur_level, &res->capture_reg->l1isp.L1_AEXP_SATUR_LEVEL); + + writel(param->aexp_ave4linesy[0], &res->capture_reg->l1isp.L1_AEXP_AVE4LINESY0); + writel(param->aexp_ave4linesy[1], &res->capture_reg->l1isp.L1_AEXP_AVE4LINESY1); + writel(param->aexp_ave4linesy[2], &res->capture_reg->l1isp.L1_AEXP_AVE4LINESY2); + writel(param->aexp_ave4linesy[3], &res->capture_reg->l1isp.L1_AEXP_AVE4LINESY3); + + return 0; +} + +/** + * hwd_viif_l1_set_irq_mask() - Set L1ISP interruption mask. + * + * @mask: mask setting + * Return: None + */ +void hwd_viif_l1_set_irq_mask(struct hwd_viif_res *res, u32 mask) +{ + writel(mask, &res->capture_reg->l1isp.L1_CRGBF_ISP_INT_MASK); +} diff --git a/drivers/media/platform/visconti/viif_controls.c b/drivers/media/platform/visconti/viif_controls.c new file mode 100644 index 00000000000..2793fb0a807 --- /dev/null +++ b/drivers/media/platform/visconti/viif_controls.c @@ -0,0 +1,1153 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2022 TOSHIBA CORPORATION + * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include +#include + +#include "viif.h" + +static int viif_main_set_rawpack_mode(struct viif_device *viif_dev, u32 *rawpack) +{ + if (vb2_is_streaming(&viif_dev->cap_dev0.vb2_vq)) + return -EBUSY; + + if (*rawpack == VIIF_RAWPACK_DISABLE) { + viif_dev->rawpack_mode = HWD_VIIF_RAWPACK_DISABLE; + return 0; + } + if (*rawpack == VIIF_RAWPACK_MSBFIRST) { + viif_dev->rawpack_mode = HWD_VIIF_RAWPACK_MSBFIRST; + return 0; + } + if (*rawpack == VIIF_RAWPACK_LSBFIRST) { + viif_dev->rawpack_mode = HWD_VIIF_RAWPACK_LSBFIRST; + return 0; + } + + return -EINVAL; +} + +static int viif_l1_set_input_mode(struct viif_device *viif_dev, + struct viif_l1_input_mode_config *input_mode) +{ + u32 mode, raw_color_filter; + unsigned long irqflags; + int ret; + + /* SDR input is not supported */ + if (input_mode->mode == VIIF_L1_INPUT_HDR) + mode = HWD_VIIF_L1_INPUT_HDR; + else if (input_mode->mode == VIIF_L1_INPUT_PWL) + mode = HWD_VIIF_L1_INPUT_PWL; + else if (input_mode->mode == VIIF_L1_INPUT_HDR_IMG_CORRECT) + mode = HWD_VIIF_L1_INPUT_HDR_IMG_CORRECT; + else if (input_mode->mode == VIIF_L1_INPUT_PWL_IMG_CORRECT) + mode = HWD_VIIF_L1_INPUT_PWL_IMG_CORRECT; + else + return -EINVAL; + + if (input_mode->raw_color_filter == VIIF_L1_RAW_GR_R_B_GB) + raw_color_filter = HWD_VIIF_L1_RAW_GR_R_B_GB; + else if (input_mode->raw_color_filter == VIIF_L1_RAW_R_GR_GB_B) + raw_color_filter = HWD_VIIF_L1_RAW_R_GR_GB_B; + else if (input_mode->raw_color_filter == VIIF_L1_RAW_B_GB_GR_R) + raw_color_filter = HWD_VIIF_L1_RAW_B_GB_GR_R; + else if (input_mode->raw_color_filter == VIIF_L1_RAW_GB_B_R_GR) + raw_color_filter = HWD_VIIF_L1_RAW_GB_B_R_GR; + else + return -EINVAL; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_input_mode(viif_dev->hwd_res, mode, input_mode->depth, + raw_color_filter); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_rgb_to_y_coef(struct viif_device *viif_dev, + struct viif_l1_rgb_to_y_coef_config *l1_rgb_to_y_coef) +{ + int ret; + unsigned long irqflags; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_rgb_to_y_coef(viif_dev->hwd_res, l1_rgb_to_y_coef->coef_r, + l1_rgb_to_y_coef->coef_g, l1_rgb_to_y_coef->coef_b); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_ag_mode(struct viif_device *viif_dev, + struct viif_l1_ag_mode_config *l1_ag_mode) +{ + int ret; + unsigned long irqflags; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_ag_mode(viif_dev->hwd_res, l1_ag_mode); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_ag(struct viif_device *viif_dev, struct viif_l1_ag_config *l1_ag) +{ + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_ag(viif_dev->hwd_res, l1_ag->gain_h, l1_ag->gain_m, l1_ag->gain_l); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_hdre(struct viif_device *viif_dev, struct viif_l1_hdre_config *l1_hdre) +{ + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_hdre(viif_dev->hwd_res, l1_hdre); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_img_extraction(struct viif_device *viif_dev, + struct viif_l1_img_extraction_config *img_extract) +{ + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_img_extraction(viif_dev->hwd_res, img_extract->input_black_gr, + img_extract->input_black_r, img_extract->input_black_b, + img_extract->input_black_gb); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +#define VISCONTI_VIIF_DPC_TABLE_SIZE 8192 +static int viif_l1_set_dpc(struct viif_device *viif_dev, struct viif_l1_dpc_config *l1_dpc) +{ + uintptr_t table_h_paddr = 0; + uintptr_t table_m_paddr = 0; + uintptr_t table_l_paddr = 0; + unsigned long irqflags; + int ret; + + if (l1_dpc->table_h_addr) { + if (copy_from_user(viif_dev->table_vaddr->dpc_table_h, + u64_to_user_ptr(l1_dpc->table_h_addr), + VISCONTI_VIIF_DPC_TABLE_SIZE)) + return -EFAULT; + table_h_paddr = (uintptr_t)viif_dev->table_paddr->dpc_table_h; + } + if (l1_dpc->table_m_addr) { + if (copy_from_user(viif_dev->table_vaddr->dpc_table_m, + u64_to_user_ptr(l1_dpc->table_m_addr), + VISCONTI_VIIF_DPC_TABLE_SIZE)) + return -EFAULT; + table_m_paddr = (uintptr_t)viif_dev->table_paddr->dpc_table_m; + } + if (l1_dpc->table_l_addr) { + if (copy_from_user(viif_dev->table_vaddr->dpc_table_l, + u64_to_user_ptr(l1_dpc->table_l_addr), + VISCONTI_VIIF_DPC_TABLE_SIZE)) + return -EFAULT; + table_l_paddr = (uintptr_t)viif_dev->table_paddr->dpc_table_l; + } + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_dpc_table_transmission(viif_dev->hwd_res, table_h_paddr, + table_m_paddr, table_l_paddr); + if (ret) + goto err; + + ret = hwd_viif_l1_set_dpc(viif_dev->hwd_res, &l1_dpc->param_h, &l1_dpc->param_m, + &l1_dpc->param_l); + +err: + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + return ret; +} + +static int +viif_l1_set_preset_white_balance(struct viif_device *viif_dev, + struct viif_l1_preset_white_balance_config *l1_preset_wb) +{ + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_preset_white_balance(viif_dev->hwd_res, l1_preset_wb->dstmaxval, + &l1_preset_wb->param_h, &l1_preset_wb->param_m, + &l1_preset_wb->param_l); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int +viif_l1_set_raw_color_noise_reduction(struct viif_device *viif_dev, + struct viif_l1_raw_color_noise_reduction_config *raw_color) +{ + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_raw_color_noise_reduction(viif_dev->hwd_res, &raw_color->param_h, + &raw_color->param_m, &raw_color->param_l); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_hdrs(struct viif_device *viif_dev, struct viif_l1_hdrs_config *hdrs) +{ + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_hdrs(viif_dev->hwd_res, hdrs); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_black_level_correction(struct viif_device *viif_dev, + struct viif_l1_black_level_correction_config *blc) +{ + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_black_level_correction(viif_dev->hwd_res, blc); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +#define VISCONTI_VIIF_LSC_TABLE_BYTES 1536 + +static int viif_l1_set_lsc(struct viif_device *viif_dev, struct viif_l1_lsc_config *l1_lsc) +{ + struct viif_l1_lsc_parabola_param lsc_para; + struct viif_l1_lsc_grid_param lsc_grid; + struct hwd_viif_l1_lsc hwd_params; + struct viif_l1_lsc lsc_params; + uintptr_t table_gr_paddr = 0; + uintptr_t table_gb_paddr = 0; + uintptr_t table_r_paddr = 0; + uintptr_t table_b_paddr = 0; + unsigned long irqflags; + int ret; + + if (!l1_lsc->param_addr) { + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_lsc(viif_dev->hwd_res, NULL); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + return ret; + } + + if (l1_lsc->table_gr_addr) { + if (copy_from_user(viif_dev->table_vaddr->lsc_table_gr, + u64_to_user_ptr(l1_lsc->table_gr_addr), + VISCONTI_VIIF_LSC_TABLE_BYTES)) + return -EFAULT; + table_gr_paddr = (uintptr_t)viif_dev->table_paddr->lsc_table_gr; + } + if (l1_lsc->table_r_addr) { + if (copy_from_user(viif_dev->table_vaddr->lsc_table_r, + u64_to_user_ptr(l1_lsc->table_r_addr), + VISCONTI_VIIF_LSC_TABLE_BYTES)) + return -EFAULT; + table_r_paddr = (uintptr_t)viif_dev->table_paddr->lsc_table_r; + } + if (l1_lsc->table_b_addr) { + if (copy_from_user(viif_dev->table_vaddr->lsc_table_b, + u64_to_user_ptr(l1_lsc->table_b_addr), + VISCONTI_VIIF_LSC_TABLE_BYTES)) + return -EFAULT; + table_b_paddr = (uintptr_t)viif_dev->table_paddr->lsc_table_b; + } + if (l1_lsc->table_gb_addr) { + if (copy_from_user(viif_dev->table_vaddr->lsc_table_gb, + u64_to_user_ptr(l1_lsc->table_gb_addr), + VISCONTI_VIIF_LSC_TABLE_BYTES)) + return -EFAULT; + table_gb_paddr = (uintptr_t)viif_dev->table_paddr->lsc_table_gb; + } + + if (copy_from_user(&lsc_params, u64_to_user_ptr(l1_lsc->param_addr), + sizeof(struct viif_l1_lsc))) + return -EFAULT; + + hwd_params.lssc_parabola_param = NULL; + hwd_params.lssc_grid_param = NULL; + + if (lsc_params.lssc_parabola_param_addr) { + if (copy_from_user(&lsc_para, u64_to_user_ptr(lsc_params.lssc_parabola_param_addr), + sizeof(struct viif_l1_lsc_parabola_param))) + return -EFAULT; + hwd_params.lssc_parabola_param = &lsc_para; + } + + if (lsc_params.lssc_grid_param_addr) { + if (copy_from_user(&lsc_grid, u64_to_user_ptr(lsc_params.lssc_grid_param_addr), + sizeof(struct viif_l1_lsc_grid_param))) + return -EFAULT; + hwd_params.lssc_grid_param = &lsc_grid; + } + + hwd_params.lssc_pwhb_r_gain_max = lsc_params.lssc_pwhb_r_gain_max; + hwd_params.lssc_pwhb_r_gain_min = lsc_params.lssc_pwhb_r_gain_min; + hwd_params.lssc_pwhb_gr_gain_max = lsc_params.lssc_pwhb_gr_gain_max; + hwd_params.lssc_pwhb_gr_gain_min = lsc_params.lssc_pwhb_gr_gain_min; + hwd_params.lssc_pwhb_gb_gain_max = lsc_params.lssc_pwhb_gb_gain_max; + hwd_params.lssc_pwhb_gb_gain_min = lsc_params.lssc_pwhb_gb_gain_min; + hwd_params.lssc_pwhb_b_gain_max = lsc_params.lssc_pwhb_b_gain_max; + hwd_params.lssc_pwhb_b_gain_min = lsc_params.lssc_pwhb_b_gain_min; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_lsc_table_transmission(viif_dev->hwd_res, table_gr_paddr, + table_r_paddr, table_b_paddr, table_gb_paddr); + if (ret) + goto err; + + ret = hwd_viif_l1_set_lsc(viif_dev->hwd_res, &hwd_params); +err: + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_main_process(struct viif_device *viif_dev, + struct viif_l1_main_process_config *mpro) +{ + struct viif_l1_color_matrix_correction color_matrix; + unsigned long irqflags; + int ret; + + if (mpro->param_addr) { + if (copy_from_user(&color_matrix, u64_to_user_ptr(mpro->param_addr), + sizeof(struct viif_l1_color_matrix_correction))) + return -EFAULT; + } + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_main_process(viif_dev->hwd_res, mpro->demosaic_mode, + mpro->damp_lsbsel, + mpro->param_addr ? &color_matrix : NULL, + mpro->dst_maxval); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_awb(struct viif_device *viif_dev, struct viif_l1_awb_config *l1_awb) +{ + struct viif_l1_awb param; + unsigned long irqflags; + int ret; + + if (l1_awb->param_addr) { + if (copy_from_user(¶m, u64_to_user_ptr(l1_awb->param_addr), + sizeof(struct viif_l1_awb))) + return -EFAULT; + } + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_awb(viif_dev->hwd_res, l1_awb->param_addr ? ¶m : NULL, + l1_awb->awhb_wbmrg, l1_awb->awhb_wbmgg, l1_awb->awhb_wbmbg); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_lock_awb_gain(struct viif_device *viif_dev, u32 *enable) +{ + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_lock_awb_gain(viif_dev->hwd_res, *enable); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_hdrc(struct viif_device *viif_dev, struct viif_l1_hdrc_config *hdrc) +{ + struct viif_l1_hdrc param; + unsigned long irqflags; + int ret; + + if (hdrc->param_addr) { + if (copy_from_user(¶m, u64_to_user_ptr(hdrc->param_addr), + sizeof(struct viif_l1_hdrc))) + return -EFAULT; + } + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_hdrc(viif_dev->hwd_res, hdrc->param_addr ? ¶m : NULL, + hdrc->hdrc_thr_sft_amt); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_hdrc_ltm(struct viif_device *viif_dev, + struct viif_l1_hdrc_ltm_config *l1_hdrc_ltm) +{ + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_hdrc_ltm(viif_dev->hwd_res, l1_hdrc_ltm); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_gamma(struct viif_device *viif_dev, struct viif_l1_gamma_config *l1_gamma) +{ + struct viif_l1_gamma param; + unsigned long irqflags; + int ret; + + if (l1_gamma->param_addr) { + if (copy_from_user(¶m, u64_to_user_ptr(l1_gamma->param_addr), + sizeof(struct viif_l1_gamma))) + return -EFAULT; + } + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_gamma(viif_dev->hwd_res, l1_gamma->param_addr ? ¶m : NULL); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int +viif_l1_set_img_quality_adjustment(struct viif_device *viif_dev, + struct viif_l1_img_quality_adjustment_config *img_quality) +{ + struct hwd_viif_l1_img_quality_adjustment hwd_img_quality; + struct viif_l1_lum_noise_reduction lum_noise; + struct viif_l1_nonlinear_contrast nonlinear; + struct viif_l1_coring_suppression coring; + struct viif_l1_edge_enhancement edge_enh; + struct viif_l1_edge_suppression edge_sup; + struct viif_l1_uv_suppression uv; + struct viif_l1_color_level color; + unsigned long irqflags; + int ret; + + hwd_img_quality.coef_cb = img_quality->coef_cb; + hwd_img_quality.coef_cr = img_quality->coef_cr; + hwd_img_quality.brightness = img_quality->brightness; + hwd_img_quality.linear_contrast = img_quality->linear_contrast; + hwd_img_quality.color_noise_reduction_enable = img_quality->color_noise_reduction_enable; + + if (img_quality->nonlinear_contrast_addr) { + if (copy_from_user(&nonlinear, + u64_to_user_ptr(img_quality->nonlinear_contrast_addr), + sizeof(struct viif_l1_nonlinear_contrast))) + return -EFAULT; + hwd_img_quality.nonlinear_contrast = &nonlinear; + } else { + hwd_img_quality.nonlinear_contrast = NULL; + } + if (img_quality->lum_noise_reduction_addr) { + if (copy_from_user(&lum_noise, + u64_to_user_ptr(img_quality->lum_noise_reduction_addr), + sizeof(struct viif_l1_lum_noise_reduction))) + return -EFAULT; + hwd_img_quality.lum_noise_reduction = &lum_noise; + } else { + hwd_img_quality.lum_noise_reduction = NULL; + } + if (img_quality->edge_enhancement_addr) { + if (copy_from_user(&edge_enh, u64_to_user_ptr(img_quality->edge_enhancement_addr), + sizeof(struct viif_l1_edge_enhancement))) + return -EFAULT; + hwd_img_quality.edge_enhancement = &edge_enh; + } else { + hwd_img_quality.edge_enhancement = NULL; + } + if (img_quality->uv_suppression_addr) { + if (copy_from_user(&uv, u64_to_user_ptr(img_quality->uv_suppression_addr), + sizeof(struct viif_l1_uv_suppression))) + return -EFAULT; + hwd_img_quality.uv_suppression = &uv; + } else { + hwd_img_quality.uv_suppression = NULL; + } + if (img_quality->coring_suppression_addr) { + if (copy_from_user(&coring, u64_to_user_ptr(img_quality->coring_suppression_addr), + sizeof(struct viif_l1_coring_suppression))) + return -EFAULT; + hwd_img_quality.coring_suppression = &coring; + } else { + hwd_img_quality.coring_suppression = NULL; + } + if (img_quality->edge_suppression_addr) { + if (copy_from_user(&edge_sup, u64_to_user_ptr(img_quality->edge_suppression_addr), + sizeof(struct viif_l1_edge_suppression))) + return -EFAULT; + hwd_img_quality.edge_suppression = &edge_sup; + } else { + hwd_img_quality.edge_suppression = NULL; + } + if (img_quality->color_level_addr) { + if (copy_from_user(&color, u64_to_user_ptr(img_quality->color_level_addr), + sizeof(struct viif_l1_color_level))) + return -EFAULT; + hwd_img_quality.color_level = &color; + } else { + hwd_img_quality.color_level = NULL; + } + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_img_quality_adjustment(viif_dev->hwd_res, &hwd_img_quality); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +static int viif_l1_set_avg_lum_generation(struct viif_device *viif_dev, + struct viif_l1_avg_lum_generation_config *l1_avg_lum) +{ + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l1_set_avg_lum_generation(viif_dev->hwd_res, l1_avg_lum); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + return ret; +} + +#define VISCONTI_VIIF_DPC_TABLE_SIZE_MIN 1024 +#define VISCONTI_VIIF_DPC_TABLE_SIZE_MAX 8192 +static int viif_l2_set_undist(struct viif_device *viif_dev, struct viif_l2_undist_config *undist) +{ + uintptr_t table_write_g_paddr = 0; + uintptr_t table_read_b_paddr = 0; + uintptr_t table_read_g_paddr = 0; + uintptr_t table_read_r_paddr = 0; + unsigned long irqflags; + int ret; + + if ((undist->size && undist->size < VISCONTI_VIIF_DPC_TABLE_SIZE_MIN) || + undist->size > VISCONTI_VIIF_DPC_TABLE_SIZE_MAX) + return -EINVAL; + + if (undist->write_g_addr) { + if (copy_from_user(viif_dev->table_vaddr->undist_write_g, + u64_to_user_ptr(undist->write_g_addr), undist->size)) + return -EFAULT; + table_write_g_paddr = (uintptr_t)viif_dev->table_paddr->undist_write_g; + } + if (undist->read_b_addr) { + if (copy_from_user(viif_dev->table_vaddr->undist_read_b, + u64_to_user_ptr(undist->read_b_addr), undist->size)) + return -EFAULT; + table_read_b_paddr = (uintptr_t)viif_dev->table_paddr->undist_read_b; + } + if (undist->read_g_addr) { + if (copy_from_user(viif_dev->table_vaddr->undist_read_g, + u64_to_user_ptr(undist->read_g_addr), undist->size)) + return -EFAULT; + table_read_g_paddr = (uintptr_t)viif_dev->table_paddr->undist_read_g; + } + if (undist->read_r_addr) { + if (copy_from_user(viif_dev->table_vaddr->undist_read_r, + u64_to_user_ptr(undist->read_r_addr), undist->size)) + return -EFAULT; + table_read_r_paddr = (uintptr_t)viif_dev->table_paddr->undist_read_r; + } + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l2_set_undist_table_transmission(viif_dev->hwd_res, table_write_g_paddr, + table_read_b_paddr, table_read_g_paddr, + table_read_r_paddr, undist->size); + if (ret) { + dev_err(viif_dev->dev, "l2_set_undist_table_transmission error. %d\n", ret); + goto err; + } + + ret = hwd_viif_l2_set_undist(viif_dev->hwd_res, &undist->param); + +err: + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + return ret; +} + +static int viif_l2_set_roi(struct viif_device *viif_dev, struct viif_l2_roi_config *roi) +{ + unsigned long irqflags; + int ret; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l2_set_roi(viif_dev->hwd_res, roi); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + return ret; +} + +static int viif_l2_set_roi_wrap(struct viif_device *viif_dev, struct viif_l2_roi_config *roi) +{ + int ret; + + ret = viif_l2_set_roi(viif_dev, roi); + if (!ret) + visconti_viif_isp_set_compose_rect(viif_dev, roi); + + return ret; +} + +#define VISCONTI_VIIF_GANMMA_TABLE_SIZE 512 +static int viif_l2_set_gamma(struct viif_device *viif_dev, struct viif_l2_gamma_config *l2_gamma) +{ + struct hwd_viif_l2_gamma_table hwd_table = { 0 }; + int pathid = l2_gamma->pathid; + unsigned long irqflags; + int postid; + int ret; + u32 i; + + if (pathid == CAPTURE_PATH_MAIN_POST0) + postid = VIIF_L2ISP_POST_0; + else if (pathid == CAPTURE_PATH_MAIN_POST1) + postid = VIIF_L2ISP_POST_1; + else + return -EINVAL; + + for (i = 0; i < 6; i++) { + if (l2_gamma->table_addr[i]) { + if (copy_from_user(viif_dev->table_vaddr->l2_gamma_table[pathid][i], + u64_to_user_ptr(l2_gamma->table_addr[i]), + VISCONTI_VIIF_GANMMA_TABLE_SIZE)) + return -EFAULT; + hwd_table.table[i] = + (uintptr_t)viif_dev->table_paddr->l2_gamma_table[pathid][i]; + } + } + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + ret = hwd_viif_l2_set_gamma_table_transmission(viif_dev->hwd_res, postid, &hwd_table); + if (ret) + goto err; + + ret = hwd_viif_l2_set_gamma(viif_dev->hwd_res, postid, l2_gamma->enable, l2_gamma->vsplit, + l2_gamma->mode); +err: + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + return ret; +} + +static int +viif_csi2rx_get_calibration_status(struct viif_device *viif_dev, + struct viif_csi2rx_dphy_calibration_status *calibration_status) +{ + int ret; + + if (!vb2_is_streaming(&viif_dev->cap_dev0.vb2_vq)) + return -EIO; + + ret = hwd_viif_csi2rx_get_calibration_status(viif_dev->hwd_res, calibration_status); + + return ret; +} + +static int viif_csi2rx_get_err_status(struct viif_device *viif_dev, + struct viif_csi2rx_err_status *csi_err) +{ + int ret; + + if (!vb2_is_streaming(&viif_dev->cap_dev0.vb2_vq)) + return -EIO; + + ret = hwd_viif_csi2rx_get_err_status(viif_dev->hwd_res, &csi_err->err_phy_fatal, + &csi_err->err_pkt_fatal, &csi_err->err_frame_fatal, + &csi_err->err_phy, &csi_err->err_pkt, + &csi_err->err_line); + + return ret; +} + +static int viif_isp_get_last_capture_status(struct viif_device *viif_dev, + struct viif_isp_capture_status *status) +{ + struct hwd_viif_l1_info l1_info; + unsigned long irqflags; + int i, j; + + spin_lock_irqsave(&viif_dev->lock, irqflags); + hwd_viif_isp_guard_start(viif_dev->hwd_res); + hwd_viif_isp_get_info(viif_dev->hwd_res, &l1_info, NULL); + hwd_viif_isp_guard_end(viif_dev->hwd_res); + spin_unlock_irqrestore(&viif_dev->lock, irqflags); + + status->l1_info.avg_lum_weight = l1_info.avg_lum_weight; + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) + status->l1_info.avg_lum_block[i][j] = l1_info.avg_lum_block[i][j]; + } + for (i = 0; i < 4; i++) + status->l1_info.avg_lum_four_line_lum[i] = l1_info.avg_lum_four_line_lum[i]; + + status->l1_info.avg_satur_pixnum = l1_info.avg_satur_pixnum; + status->l1_info.avg_black_pixnum = l1_info.avg_black_pixnum; + status->l1_info.awb_ave_u = l1_info.awb_ave_u; + status->l1_info.awb_ave_v = l1_info.awb_ave_v; + status->l1_info.awb_accumulated_pixel = l1_info.awb_accumulated_pixel; + status->l1_info.awb_gain_r = l1_info.awb_gain_r; + status->l1_info.awb_gain_g = l1_info.awb_gain_g; + status->l1_info.awb_gain_b = l1_info.awb_gain_b; + status->l1_info.awb_status_u = l1_info.awb_status_u; + status->l1_info.awb_status_v = l1_info.awb_status_v; + + return 0; +} + +static int viif_isp_get_reported_errors(struct viif_device *viif_dev, + struct viif_reported_errors *status) +{ + status->main = viif_dev->reported_err_main; + status->sub = viif_dev->reported_err_sub; + status->csi2rx = viif_dev->reported_err_csi2rx; + viif_dev->reported_err_main = 0; + viif_dev->reported_err_sub = 0; + viif_dev->reported_err_csi2rx = 0; + + return 0; +} + +/* ===== v4l2 subdevice control handlers ===== */ +#define COMPOUND_TYPE_SAMPLE01 0x0280 + +static int visconti_viif_isp_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct viif_device *viif_dev = ctrl->priv; + + pr_info("isp_set_ctrl: %s", ctrl->name); + if (pm_runtime_status_suspended(viif_dev->dev)) { + pr_info("warning: visconti viif HW is not powered"); + return 0; + } + + switch (ctrl->id) { + case V4L2_CID_VISCONTI_VIIF_MAIN_SET_RAWPACK_MODE: + return viif_main_set_rawpack_mode(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_INPUT_MODE: + return viif_l1_set_input_mode(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_RGB_TO_Y_COEF: + return viif_l1_set_rgb_to_y_coef(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AG_MODE: + return viif_l1_set_ag_mode(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AG: + return viif_l1_set_ag(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRE: + return viif_l1_set_hdre(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_IMG_EXTRACTION: + return viif_l1_set_img_extraction(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_DPC: + return viif_l1_set_dpc(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_PRESET_WHITE_BALANCE: + return viif_l1_set_preset_white_balance(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_RAW_COLOR_NOISE_REDUCTION: + return viif_l1_set_raw_color_noise_reduction(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRS: + return viif_l1_set_hdrs(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_BLACK_LEVEL_CORRECTION: + return viif_l1_set_black_level_correction(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_LSC: + return viif_l1_set_lsc(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_MAIN_PROCESS: + return viif_l1_set_main_process(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AWB: + return viif_l1_set_awb(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_LOCK_AWB_GAIN: + return viif_l1_lock_awb_gain(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRC: + return viif_l1_set_hdrc(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRC_LTM: + return viif_l1_set_hdrc_ltm(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_GAMMA: + return viif_l1_set_gamma(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_IMG_QUALITY_ADJUSTMENT: + return viif_l1_set_img_quality_adjustment(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AVG_LUM_GENERATION: + return viif_l1_set_avg_lum_generation(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_UNDIST: + return viif_l2_set_undist(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_ROI: + return viif_l2_set_roi_wrap(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_GAMMA: + return viif_l2_set_gamma(viif_dev, ctrl->p_new.p); + default: + pr_info("unknown_ctrl: id=%08X val=%d", ctrl->id, ctrl->val); + break; + } + return 0; +} + +static int visconti_viif_isp_get_ctrl(struct v4l2_ctrl *ctrl) +{ + struct viif_device *viif_dev = ctrl->priv; + + pr_info("isp_get_ctrl: %s", ctrl->name); + if (pm_runtime_status_suspended(viif_dev->dev)) { + pr_info("warning: visconti viif HW is not powered"); + return 0; + } + + switch (ctrl->id) { + case V4L2_CID_VISCONTI_VIIF_CSI2RX_GET_CALIBRATION_STATUS: + return viif_csi2rx_get_calibration_status(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_CSI2RX_GET_ERR_STATUS: + return viif_csi2rx_get_err_status(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_GET_LAST_CAPTURE_STATUS: + return viif_isp_get_last_capture_status(viif_dev, ctrl->p_new.p); + case V4L2_CID_VISCONTI_VIIF_GET_REPORTED_ERRORS: + return viif_isp_get_reported_errors(viif_dev, ctrl->p_new.p); + default: + pr_info("unknown_ctrl: id=%08X val=%d", ctrl->id, ctrl->val); + break; + } + return 0; +} + +/* ===== register v4l2 subdevice controls ===== */ +static bool visconti_viif_isp_custom_ctrl_equal(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2) +{ + return !memcmp(ptr1.p_const, ptr2.p_const, ctrl->elem_size); +} + +static void visconti_viif_isp_custom_ctrl_init(const struct v4l2_ctrl *ctrl, u32 idx, + union v4l2_ctrl_ptr ptr) +{ + if (ctrl->p_def.p_const) + memcpy(ptr.p, ctrl->p_def.p_const, ctrl->elem_size); + else + memset(ptr.p, 0, ctrl->elem_size); +} + +static void visconti_viif_isp_custom_ctrl_log(const struct v4l2_ctrl *ctrl) +{ +} + +static int visconti_viif_isp_custom_ctrl_validate(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr) +{ + pr_info("std_validate: %s", ctrl->name); + return 0; +} + +static const struct v4l2_ctrl_type_ops custom_type_ops = { + .equal = visconti_viif_isp_custom_ctrl_equal, + .init = visconti_viif_isp_custom_ctrl_init, + .log = visconti_viif_isp_custom_ctrl_log, + .validate = visconti_viif_isp_custom_ctrl_validate, +}; + +static const struct v4l2_ctrl_ops visconti_viif_isp_ctrl_ops = { + .g_volatile_ctrl = visconti_viif_isp_get_ctrl, + .s_ctrl = visconti_viif_isp_set_ctrl, +}; + +/* ----- control handler ----- */ +#define CTRL_CONFIG_DEFAULT_ENTRY \ + .ops = &visconti_viif_isp_ctrl_ops, .type_ops = &custom_type_ops, \ + .type = COMPOUND_TYPE_SAMPLE01, .flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE + +#define CTRL_CONFIG_RDONLY_ENTRY \ + .ops = &visconti_viif_isp_ctrl_ops, .type_ops = &custom_type_ops, \ + .type = COMPOUND_TYPE_SAMPLE01, .flags = V4L2_CTRL_FLAG_VOLATILE + +static const struct v4l2_ctrl_config visconti_viif_isp_ctrl_config[] = { + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_MAIN_SET_RAWPACK_MODE, + .name = "rawpack_mode", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(u32), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_INPUT_MODE, + .name = "l1_input_mode", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_input_mode_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_RGB_TO_Y_COEF, + .name = "l1_rgb_to_y_coef", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_rgb_to_y_coef_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AG_MODE, + .name = "l1_ag_mode", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_ag_mode_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AG, + .name = "l1_ag", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_ag_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRE, + .name = "l1_hdre", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_hdre_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_IMG_EXTRACTION, + .name = "l1_img_extraction", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_img_extraction_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_DPC, + .name = "l1_dpc", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_dpc_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_PRESET_WHITE_BALANCE, + .name = "l1_preset_white_balance", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_preset_white_balance_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_RAW_COLOR_NOISE_REDUCTION, + .name = "l1_raw_color_noise_reduction", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_raw_color_noise_reduction_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRS, + .name = "l1_set_hdrs", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_hdrs_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_BLACK_LEVEL_CORRECTION, + .name = "l1_black_level_correction", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_black_level_correction_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_LSC, + .name = "l1_lsc", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_lsc_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_MAIN_PROCESS, + .name = "l1_main_process", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_main_process_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AWB, + .name = "l1_awb", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_awb_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_LOCK_AWB_GAIN, + .name = "l1_lock_awb_gain", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(u32), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRC, + .name = "l1_hdrc", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_hdrc_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRC_LTM, + .name = "l1_hdrc_ltm", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_hdrc_ltm_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_GAMMA, + .name = "l1_gamma", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_gamma_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_IMG_QUALITY_ADJUSTMENT, + .name = "l1_img_quality_adjustment", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_img_quality_adjustment_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AVG_LUM_GENERATION, + .name = "l1_avg_lum", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l1_avg_lum_generation_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_UNDIST, + .name = "l2_undist", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l2_undist_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_ROI, + .name = "l2_roi", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l2_roi_config), + }, + { + CTRL_CONFIG_DEFAULT_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_GAMMA, + .name = "l2_gamma", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_l2_gamma_config), + }, + { + CTRL_CONFIG_RDONLY_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_CSI2RX_GET_CALIBRATION_STATUS, + .name = "csi2rx_calibration_status", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_csi2rx_dphy_calibration_status), + }, + { + CTRL_CONFIG_RDONLY_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_CSI2RX_GET_ERR_STATUS, + .name = "csi2rx_err_status", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_csi2rx_err_status), + }, + { + CTRL_CONFIG_RDONLY_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_GET_LAST_CAPTURE_STATUS, + .name = "last_capture_status", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_isp_capture_status), + }, + { + CTRL_CONFIG_RDONLY_ENTRY, + .id = V4L2_CID_VISCONTI_VIIF_GET_REPORTED_ERRORS, + .name = "reported errors", + .p_def = { .p_const = NULL }, + .elem_size = sizeof(struct viif_reported_errors), + }, +}; + +int visconti_viif_isp_init_controls(struct viif_device *viif_dev) +{ + struct v4l2_ctrl_handler *ctrl_handler = &viif_dev->isp_subdev.ctrl_handler; + int ret; + int i; + + ret = v4l2_ctrl_handler_init(ctrl_handler, 10); + if (ret) { + dev_err(viif_dev->dev, "failed on v4l2_ctrl_handler_init"); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(visconti_viif_isp_ctrl_config); i++) { + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_new_custom(ctrl_handler, &visconti_viif_isp_ctrl_config[i], + viif_dev); + if (!ctrl) { + dev_err(viif_dev->dev, "failed to add ctrl crop: %d", ctrl_handler->error); + return ctrl_handler->error; + } + } + + viif_dev->isp_subdev.sd.ctrl_handler = &viif_dev->isp_subdev.ctrl_handler; + return 0; +} diff --git a/drivers/media/platform/visconti/viif_isp.c b/drivers/media/platform/visconti/viif_isp.c index 9314e6e8661..9aeb8bcab9b 100644 --- a/drivers/media/platform/visconti/viif_isp.c +++ b/drivers/media/platform/visconti/viif_isp.c @@ -818,6 +818,8 @@ int visconti_viif_isp_register(struct viif_device *viif_dev) mutex_init(&viif_dev->isp_subdev.ops_lock); + visconti_viif_isp_init_controls(viif_dev); + ret = media_entity_pads_init(&sd->entity, 4, pads); if (ret) { dev_err(viif_dev->dev, "Failed on media_entity_pads_init\n"); From patchwork Wed Jan 11 02:24:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuji Ishikawa X-Patchwork-Id: 641550 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B7C2EC61DB3 for ; Wed, 11 Jan 2023 02:31:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235823AbjAKCbE (ORCPT ); Tue, 10 Jan 2023 21:31:04 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42408 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235759AbjAKCaq (ORCPT ); Tue, 10 Jan 2023 21:30:46 -0500 Received: from mo-csw.securemx.jp (mo-csw1114.securemx.jp [210.130.202.156]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 38EA1E0C5; Tue, 10 Jan 2023 18:30:38 -0800 (PST) Received: by mo-csw.securemx.jp (mx-mo-csw1114) id 30B2U9SS022089; Wed, 11 Jan 2023 11:30:10 +0900 X-Iguazu-Qid: 2wGr0AvYwOw8cBjT8K X-Iguazu-QSIG: v=2; s=0; t=1673404209; q=2wGr0AvYwOw8cBjT8K; m=J9eox3MjUAHDWds2UJwuGiEyzf9MmEIhtdHKXBniMU8= Received: from imx2-a.toshiba.co.jp (imx2-a.toshiba.co.jp [106.186.93.35]) by relay.securemx.jp (mx-mr1110) id 30B2U6VD010386 (version=TLSv1.2 cipher=AES128-GCM-SHA256 bits=128 verify=NOT); Wed, 11 Jan 2023 11:30:06 +0900 X-SA-MID: 52825689 From: Yuji Ishikawa To: Hans Verkuil , Laurent Pinchart , Mauro Carvalho Chehab , Nobuhiro Iwamatsu , Rob Herring , Krzysztof Kozlowski , "Rafael J . Wysocki" , Mark Brown Cc: linux-media@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, yuji2.ishikawa@toshiba.co.jp Subject: [PATCH v5 5/6] documentation: media: add documentation for Toshiba Visconti Video Input Interface driver Date: Wed, 11 Jan 2023 11:24:32 +0900 X-TSB-HOP2: ON Message-Id: <20230111022433.25950-6-yuji2.ishikawa@toshiba.co.jp> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230111022433.25950-1-yuji2.ishikawa@toshiba.co.jp> References: <20230111022433.25950-1-yuji2.ishikawa@toshiba.co.jp> Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Added basic description of Video Input Interface driver of Toshiba Visconti architecture. It includes hardware organization, structure of the driver and description of vendor specific V4L2 controls to configure the embedded image signal processor. Signed-off-by: Yuji Ishikawa --- Changelog v3: - Newly add documentation to describe SW and HW Changelog v4: - no change Changelog v5: - no change --- .../driver-api/media/drivers/index.rst | 1 + .../media/drivers/visconti-viif.rst | 455 ++++++++++++++++++ 2 files changed, 456 insertions(+) create mode 100644 Documentation/driver-api/media/drivers/visconti-viif.rst diff --git a/Documentation/driver-api/media/drivers/index.rst b/Documentation/driver-api/media/drivers/index.rst index 32406490557..ea46cab34ea 100644 --- a/Documentation/driver-api/media/drivers/index.rst +++ b/Documentation/driver-api/media/drivers/index.rst @@ -26,6 +26,7 @@ Video4Linux (V4L) drivers sh_mobile_ceu_camera tuners vimc-devel + visconti-viif zoran ccs/ccs diff --git a/Documentation/driver-api/media/drivers/visconti-viif.rst b/Documentation/driver-api/media/drivers/visconti-viif.rst new file mode 100644 index 00000000000..f139f60f9cb --- /dev/null +++ b/Documentation/driver-api/media/drivers/visconti-viif.rst @@ -0,0 +1,455 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================================ +Visconti Video Input Interface (VIIF) Driver +============================================ + +Overview +======== + +The Visconti VIIF Hardware +-------------------------- + +The Visconti Video Input Interface (VIIF) hardware is a proprietary videocapture device of Toshiba. +Following function modules are integrated: + +* MIPI CSI2 receiver (CSI2RX) +* L1 Image Signal Processor (L1ISP) + + * Correction, enhancement, adjustment on RAW pictures. + +* L2 Image Signal Processor (L2ISP) + + * Lens distortion correction + * Scaling + * Cropping + +* Video DMAC + + * format picture (RGB, YUV, Grayscale, ...) + * write picture into DRAM + +Visconti5 SoC has two VIIF hardware instances. + +software architecture +--------------------- + +The Visconti VIIF driver is composed of following components: + +* (image sensor driver) +* Visconti ISP subdevice driver + + * corresponding to CSI2RX, L1ISP, L2ISP (Lens distortion correction, Scaling) + +* Visconti Capture V4L2 device driver + + * corresponding to L2ISP (Cropping) and Video DMAC + * multiple output videobuf queues + + * main path0 (RGB, YUV, Grayscale, ...) + * main path1 (RGB, YUV, Grayscale, ...) + * sub path (RAW picture) + +:: + + +-----------+ +------------------------+ +-------------------------+ + | Sensor | | ISP | | Capture MAIN PATH0 | + | subdevice | ---- | subdevice | --+-- | V4L2 device | + | (IMX219) | | (CSI2RX, L1ISP, L2ISP) | | | (L2ISP crop, VideoDMAC) | + +-----------+ +------------------------+ | +-------------------------+ + | + | +-------------------------+ + | | Capture MAIN PATH1 | + +-- | V4L2 device | + | | (L2ISP crop, VideoDMAC) | + | +-------------------------+ + | + | +-------------------------+ + | | Capture SUB PATH | + +-- | V4L2 device | + | (L2ISP crop, VideoDMAC) | + +-------------------------+ + + +The VIIF driver provides following device nodes for Visconti5 SoC: + +* VIIF0 + + * /dev/media0 + * /dev/video0 (main path0) + * /dev/video1 (main path1) + * /dev/video2 (sub path) + +* VIIF1 + + * /dev/media1 + * /dev/video3 + * /dev/video4 + * /dev/video5 + +Use of coherent memory +---------------------- + +Visconti5 SoC has two independent DDR SDRAM controllers. +Each controller is mapped to 36bit address space. + +Accelerator bus masters have two paths to access memory; +one is directly connected to SDRAM controller, +the another is connected via a cache coherency bus +which keeps coherency among CPUs. + +From acclerators and CPUs, the address map is following: + +* 0x0_8000_0000 DDR0 direct access +* 0x4_8000_0000 DDR0 coherency bus +* 0x8_8000_0000 DDR1 direct access +* 0xC_8000_0000 DDR1 coherency bus + +The base address can be specified with "memory" and "reserved-memory" elements +in a device tree description. +It's not recommended to mix direct address and coherent address. + +The Visconti5 VIIF driver always use only direct address to configure Video DMACs of the hardware. +This design is to avoid great performance loss at coherency bus caused by massive memory access. +You should not put the dma_coherent attribute to viif element in device tree. +Cache operations are done automatically by videobuf2 driver. + +Tested environment +------------------ + +The Visconti VIIF driver was tested with following items: + +* IMX219 image sensor +* IMX335 image sensor +* TC358743 HDMI to MIPI CSI2 converter + +IOCTLs +====== + +Following public IOCTLs are supported + +* VIDIOC_QUERYCAP +* VIDIOC_ENUM_FMT +* VIDIOC_TRY_FMT +* VIDIOC_S_FMT +* VIDIOC_G_FMT +* VIDIOC_ENUM_INPUT +* VIDIOC_G_INPUT +* VIDIOC_S_INPUT +* VIDIOC_G_SELECTION +* VIDIOC_S_SELECTION +* VIDIOC_DV_TIMINGS_CAP +* VIDIOC_ENUM_DV_TIMINGS +* VIDIOC_G_DV_TIMINGS +* VIDIOC_S_DV_TIMINGS +* VIDIOC_QUERY_DV_TIMINGS +* VIDIOC_G_EDID +* VIDIOC_S_EDID +* VIDIOC_G_PARM +* VIDIOC_S_PARM +* VIDIOC_ENUM_FRAMESIZES +* VIDIOC_ENUM_FRAMEINTERVALS +* VIDIOC_G_EXT_CTRLS +* VIDIOC_S_EXT_CTRLS + +Vendor specific v4l2 controls +============================= + +.. _V4L2_CID_VISCONTI_VIIF_MAIN_SET_RAWPACK_MODE: + +V4L2_CID_VISCONTI_VIIF_MAIN_SET_RAWPACK_MODE +-------------------------------------------- + +This control sets the format to pack multiple RAW pixel values into a word. + +This control accepts a __u32 value defined as `enum viif_rawpack_mode`. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_INPUT_MODE: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_INPUT_MODE +-------------------------------------------- + +This control sets L1ISP preprocessing mode for RAW input images. + +This control accepts a `struct viif_l1_input_mode_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_RGB_TO_Y_COEF: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_RGB_TO_Y_COEF +----------------------------------------------- + +This control sets parameters to yield Y value from RGB pixel values. + +This control accepts a `struct viif_l1_rgb_to_y_coef_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AG_MODE: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AG_MODE +----------------------------------------- + +This control sets rules for yielding analog gains for each feature in L1ISP. +Related features are: + +* Optical Black Clamp Correction (OBCC) +* Defect Pixel Correction (DPC) +* RAW Color Noise Reduction (RCNR) +* Lens Shading Correction (LSC) +* Color matrix correction (MPRO) +* Image quality adjustment (VPRO) + +The base gain is brought by V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AG control. + +This control accepts a `struct viif_l1_ag_mode_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AG: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AG +------------------------------------ + +This control sets base analog gain values comonly used in L1ISP features. +Each analog gain in some features also refers V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AG_MODE control. + +This control accepts a `struct viif_l1_ag_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRE: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRE +-------------------------------------- + +This controls sets parameters for HDR Expansion feature. + +This control accepts a `struct viif_l1_hdre_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_IMG_EXTRACTION: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_IMG_EXTRACTION +------------------------------------------------ + +This control sets black level parameters for L1ISP inputs. + +This control accepts a `struct viif_l1_img_extraction_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_DPC: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_DPC +------------------------------------- + +This control sets parameters for Defect Pixel Correction. + +This control accepts a `struct viif_l1_dpc_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_PRESET_WHITE_BALANCE: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_PRESET_WHITE_BALANCE +------------------------------------------------------ + +This control sets parameters for white balance. + +This control accepts a `struct viif_l1_preset_white_balance_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_RAW_COLOR_NOISE_REDUCTION: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_RAW_COLOR_NOISE_REDUCTION +----------------------------------------------------------- + +This control sets parameters for RAW color noise reduction (RCNR) feature. + +This control accepts a `struct viif_l1_raw_color_noise_reduction_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRS: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRS +-------------------------------------- + +This control sets parameters for HDR synthesis. + +This control accepts a `struct viif_l1_hdrs_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_BLACK_LEVEL_CORRECTION: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_BLACK_LEVEL_CORRECTION +-------------------------------------------------------- + +This control sets parameters for black level correction feature. + +This control accepts a `struct viif_l1_black_level_correction_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_LSC: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_LSC +------------------------------------- + +This control sets parameters for Lens Shading Correction feature. +L1ISP supports 2 correction methods: + +* parabola shading +* grid shading + +This control accepts a `struct viif_l1_lsc_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_MAIN_PROCESS: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_MAIN_PROCESS +---------------------------------------------- + +This controls sets parameter for the MAIN PROCESS feature which is composed of: + +* demosaic +* color matrix correction + +This control accepts a `struct viif_l1_main_process_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AWB: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AWB +------------------------------------- + +This control sets parameter for auto white balance feature. + +This control accepts a `struct viif_l1_awb_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_LOCK_AWB_GAIN: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_LOCK_AWB_GAIN +------------------------------------------- + +This control requests enable/disable of lock for AWB gain. + +This control accepts a u32 value; 0 for disable lock, 1 for enable lock. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRC: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRC +-------------------------------------- + +This control sets parameter for HDR Compression feature. + +This control accepts a `struct viif_l1_hdrc_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRC_LTM: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_HDRC_LTM +------------------------------------------ + +This control sets parameter for HDR Compression Local Tone Mapping feature. + +This control accepts a `struct viif_l1_hdrc_ltm_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_GAMMA: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_GAMMA +--------------------------------------- + +This control sets parameter for gamma correction at L1ISP. + +This control accepts a `struct viif_l1_gamma_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_IMG_QUALITY_ADJUSTMENT: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_IMG_QUALITY_ADJUSTMENT +-------------------------------------------------------- + +This control sets parameter for VPRO feature which is composed of: + +* luminance adjustment: + + * brightness adjustment + * linear contrast adjusment + * nonlinear contrast adjustment + * luminance noise reduction + * edge enhancement + +* chroma adjustment: + + * chroma suppression + * color level adjustment + * chroma noise reduction + * coring suppression + * edge chroma suppression + * color noise reduction + +This control accepts a `struct viif_l1_img_quality_adjustment_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AVG_LUM_GENERATION: + +V4L2_CID_VISCONTI_VIIF_ISP_L1_SET_AVG_LUM_GENERATION +---------------------------------------------------- + +This control sets parameter for average luminance statistics feature. + +This control accepts a `struct viif_l1_avg_lum_generation_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_UNDIST: + +V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_UNDIST +---------------------------------------- + +This control sets parameter for the lens undistortion feature of L2ISP. +Lens undistortion parameters are defined as either or combination of polinomial parameter and grid table. + +This control accepts a `struct viif_l2_undist_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_ROI: + +V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_ROI +------------------------------------- + +This control sets dimensions of intermediate images and scaling parameter of L2ISP. +If you are inserested in cropping pictures, +you should use VIDIOC_S_SELECTION ioctl for the corresponding capture device. + +This control accepts a `struct viif_l2_roi_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_GAMMA: + +V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_GAMMA +--------------------------------------- + +This control sets gamma parameter for L2ISP. + +This control accepts a `struct viif_l2_gamma_config` instance. + +.. _V4L2_CID_VISCONTI_VIIF_CSI2RX_GET_CALIBRATION_STATUS: + +V4L2_CID_VISCONTI_VIIF_CSI2RX_GET_CALIBRATION_STATUS +---------------------------------------------------- + +This control provides CSI2 receiver calibration status. + +This control fills a `struct viif_csi2rx_cal_status` instance with current status. + +.. _V4L2_CID_VISCONTI_VIIF_CSI2RX_GET_ERR_STATUS: + +V4L2_CID_VISCONTI_VIIF_CSI2RX_GET_ERR_STATUS +-------------------------------------------- + +This control provides CSI2 receiver error description. + +This control fills a `struct viif_csi2rx_err_status` instance with current status. + +.. _V4L2_CID_VISCONTI_VIIF_GET_LAST_CAPTURE_STATUS: + +V4L2_CID_VISCONTI_VIIF_GET_LAST_CAPTURE_STATUS +---------------------------------------------- + +This control provides status information for the last captured frame. + +This control fills a `struct viif_l1_info` instance with current status. + +.. _V4L2_CID_VISCONTI_VIIF_GET_REPORTED_ERRORS: + +V4L2_CID_VISCONTI_VIIF_GET_REPORTED_ERRORS +------------------------------------------ + +This control provides error information since the last read of this control. + +This control fills a `struct viif_reported_errors` instance with current status. + +Structures +========== + +.. kernel-doc:: include/uapi/linux/visconti_viif.h + +Code example +============ +