18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vsp1_hgt.c -- R-Car VSP1 Histogram Generator 2D 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Renesas Electronics Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contact: Niklas Söderlund (niklas.soderlund@ragnatech.se) 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/gfp.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 148c2ecf20Sopenharmony_ci#include <media/videobuf2-vmalloc.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "vsp1.h" 178c2ecf20Sopenharmony_ci#include "vsp1_dl.h" 188c2ecf20Sopenharmony_ci#include "vsp1_hgt.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define HGT_DATA_SIZE ((2 + 6 * 32) * 4) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 238c2ecf20Sopenharmony_ci * Device Access 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic inline u32 vsp1_hgt_read(struct vsp1_hgt *hgt, u32 reg) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci return vsp1_read(hgt->histo.entity.vsp1, reg); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic inline void vsp1_hgt_write(struct vsp1_hgt *hgt, 328c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb, u32 reg, u32 data) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci vsp1_dl_body_write(dlb, reg, data); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 388c2ecf20Sopenharmony_ci * Frame End Handler 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_civoid vsp1_hgt_frame_end(struct vsp1_entity *entity) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct vsp1_hgt *hgt = to_hgt(&entity->subdev); 448c2ecf20Sopenharmony_ci struct vsp1_histogram_buffer *buf; 458c2ecf20Sopenharmony_ci unsigned int m; 468c2ecf20Sopenharmony_ci unsigned int n; 478c2ecf20Sopenharmony_ci u32 *data; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci buf = vsp1_histogram_buffer_get(&hgt->histo); 508c2ecf20Sopenharmony_ci if (!buf) 518c2ecf20Sopenharmony_ci return; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci data = buf->addr; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci *data++ = vsp1_hgt_read(hgt, VI6_HGT_MAXMIN); 568c2ecf20Sopenharmony_ci *data++ = vsp1_hgt_read(hgt, VI6_HGT_SUM); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci for (m = 0; m < 6; ++m) 598c2ecf20Sopenharmony_ci for (n = 0; n < 32; ++n) 608c2ecf20Sopenharmony_ci *data++ = vsp1_hgt_read(hgt, VI6_HGT_HISTO(m, n)); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci vsp1_histogram_buffer_complete(&hgt->histo, buf, HGT_DATA_SIZE); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 668c2ecf20Sopenharmony_ci * Controls 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define V4L2_CID_VSP1_HGT_HUE_AREAS (V4L2_CID_USER_BASE | 0x1001) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int hgt_hue_areas_try_ctrl(struct v4l2_ctrl *ctrl) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci const u8 *values = ctrl->p_new.p_u8; 748c2ecf20Sopenharmony_ci unsigned int i; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * The hardware has constraints on the hue area boundaries beyond the 788c2ecf20Sopenharmony_ci * control min, max and step. The values must match one of the following 798c2ecf20Sopenharmony_ci * expressions. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * 0L <= 0U <= 1L <= 1U <= 2L <= 2U <= 3L <= 3U <= 4L <= 4U <= 5L <= 5U 828c2ecf20Sopenharmony_ci * 0U <= 1L <= 1U <= 2L <= 2U <= 3L <= 3U <= 4L <= 4U <= 5L <= 5U <= 0L 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * Start by verifying the common part... 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci for (i = 1; i < (HGT_NUM_HUE_AREAS * 2) - 1; ++i) { 878c2ecf20Sopenharmony_ci if (values[i] > values[i+1]) 888c2ecf20Sopenharmony_ci return -EINVAL; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* ... and handle 0L separately. */ 928c2ecf20Sopenharmony_ci if (values[0] > values[1] && values[11] > values[0]) 938c2ecf20Sopenharmony_ci return -EINVAL; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int hgt_hue_areas_s_ctrl(struct v4l2_ctrl *ctrl) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct vsp1_hgt *hgt = container_of(ctrl->handler, struct vsp1_hgt, 1018c2ecf20Sopenharmony_ci ctrls); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci memcpy(hgt->hue_areas, ctrl->p_new.p_u8, sizeof(hgt->hue_areas)); 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops hgt_hue_areas_ctrl_ops = { 1088c2ecf20Sopenharmony_ci .try_ctrl = hgt_hue_areas_try_ctrl, 1098c2ecf20Sopenharmony_ci .s_ctrl = hgt_hue_areas_s_ctrl, 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config hgt_hue_areas = { 1138c2ecf20Sopenharmony_ci .ops = &hgt_hue_areas_ctrl_ops, 1148c2ecf20Sopenharmony_ci .id = V4L2_CID_VSP1_HGT_HUE_AREAS, 1158c2ecf20Sopenharmony_ci .name = "Boundary Values for Hue Area", 1168c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_U8, 1178c2ecf20Sopenharmony_ci .min = 0, 1188c2ecf20Sopenharmony_ci .max = 255, 1198c2ecf20Sopenharmony_ci .def = 0, 1208c2ecf20Sopenharmony_ci .step = 1, 1218c2ecf20Sopenharmony_ci .dims = { 12 }, 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1258c2ecf20Sopenharmony_ci * VSP1 Entity Operations 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void hgt_configure_stream(struct vsp1_entity *entity, 1298c2ecf20Sopenharmony_ci struct vsp1_pipeline *pipe, 1308c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl, 1318c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct vsp1_hgt *hgt = to_hgt(&entity->subdev); 1348c2ecf20Sopenharmony_ci struct v4l2_rect *compose; 1358c2ecf20Sopenharmony_ci struct v4l2_rect *crop; 1368c2ecf20Sopenharmony_ci unsigned int hratio; 1378c2ecf20Sopenharmony_ci unsigned int vratio; 1388c2ecf20Sopenharmony_ci u8 lower; 1398c2ecf20Sopenharmony_ci u8 upper; 1408c2ecf20Sopenharmony_ci unsigned int i; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci crop = vsp1_entity_get_pad_selection(entity, entity->config, 1438c2ecf20Sopenharmony_ci HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); 1448c2ecf20Sopenharmony_ci compose = vsp1_entity_get_pad_selection(entity, entity->config, 1458c2ecf20Sopenharmony_ci HISTO_PAD_SINK, 1468c2ecf20Sopenharmony_ci V4L2_SEL_TGT_COMPOSE); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci vsp1_hgt_write(hgt, dlb, VI6_HGT_OFFSET, 1518c2ecf20Sopenharmony_ci (crop->left << VI6_HGT_OFFSET_HOFFSET_SHIFT) | 1528c2ecf20Sopenharmony_ci (crop->top << VI6_HGT_OFFSET_VOFFSET_SHIFT)); 1538c2ecf20Sopenharmony_ci vsp1_hgt_write(hgt, dlb, VI6_HGT_SIZE, 1548c2ecf20Sopenharmony_ci (crop->width << VI6_HGT_SIZE_HSIZE_SHIFT) | 1558c2ecf20Sopenharmony_ci (crop->height << VI6_HGT_SIZE_VSIZE_SHIFT)); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci mutex_lock(hgt->ctrls.lock); 1588c2ecf20Sopenharmony_ci for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) { 1598c2ecf20Sopenharmony_ci lower = hgt->hue_areas[i*2 + 0]; 1608c2ecf20Sopenharmony_ci upper = hgt->hue_areas[i*2 + 1]; 1618c2ecf20Sopenharmony_ci vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i), 1628c2ecf20Sopenharmony_ci (lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) | 1638c2ecf20Sopenharmony_ci (upper << VI6_HGT_HUE_AREA_UPPER_SHIFT)); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci mutex_unlock(hgt->ctrls.lock); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci hratio = crop->width * 2 / compose->width / 3; 1688c2ecf20Sopenharmony_ci vratio = crop->height * 2 / compose->height / 3; 1698c2ecf20Sopenharmony_ci vsp1_hgt_write(hgt, dlb, VI6_HGT_MODE, 1708c2ecf20Sopenharmony_ci (hratio << VI6_HGT_MODE_HRATIO_SHIFT) | 1718c2ecf20Sopenharmony_ci (vratio << VI6_HGT_MODE_VRATIO_SHIFT)); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const struct vsp1_entity_operations hgt_entity_ops = { 1758c2ecf20Sopenharmony_ci .configure_stream = hgt_configure_stream, 1768c2ecf20Sopenharmony_ci .destroy = vsp1_histogram_destroy, 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1808c2ecf20Sopenharmony_ci * Initialization and Cleanup 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic const unsigned int hgt_mbus_formats[] = { 1848c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_AHSV8888_1X32, 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistruct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct vsp1_hgt *hgt; 1908c2ecf20Sopenharmony_ci int ret; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci hgt = devm_kzalloc(vsp1->dev, sizeof(*hgt), GFP_KERNEL); 1938c2ecf20Sopenharmony_ci if (hgt == NULL) 1948c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Initialize the control handler. */ 1978c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&hgt->ctrls, 1); 1988c2ecf20Sopenharmony_ci v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Initialize the video device and queue for statistics data. */ 2038c2ecf20Sopenharmony_ci ret = vsp1_histogram_init(vsp1, &hgt->histo, VSP1_ENTITY_HGT, "hgt", 2048c2ecf20Sopenharmony_ci &hgt_entity_ops, hgt_mbus_formats, 2058c2ecf20Sopenharmony_ci ARRAY_SIZE(hgt_mbus_formats), 2068c2ecf20Sopenharmony_ci HGT_DATA_SIZE, V4L2_META_FMT_VSP1_HGT); 2078c2ecf20Sopenharmony_ci if (ret < 0) { 2088c2ecf20Sopenharmony_ci vsp1_entity_destroy(&hgt->histo.entity); 2098c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(&hgt->ctrls); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return hgt; 2158c2ecf20Sopenharmony_ci} 216