18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vsp1_hgo.c -- R-Car VSP1 Histogram Generator 1D 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Renesas Electronics Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 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_hgo.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define HGO_DATA_SIZE ((2 + 256) * 4) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 238c2ecf20Sopenharmony_ci * Device Access 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic inline u32 vsp1_hgo_read(struct vsp1_hgo *hgo, u32 reg) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci return vsp1_read(hgo->histo.entity.vsp1, reg); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic inline void vsp1_hgo_write(struct vsp1_hgo *hgo, 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_hgo_frame_end(struct vsp1_entity *entity) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct vsp1_hgo *hgo = to_hgo(&entity->subdev); 448c2ecf20Sopenharmony_ci struct vsp1_histogram_buffer *buf; 458c2ecf20Sopenharmony_ci unsigned int i; 468c2ecf20Sopenharmony_ci size_t size; 478c2ecf20Sopenharmony_ci u32 *data; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci buf = vsp1_histogram_buffer_get(&hgo->histo); 508c2ecf20Sopenharmony_ci if (!buf) 518c2ecf20Sopenharmony_ci return; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci data = buf->addr; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (hgo->num_bins == 256) { 568c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_MAXMIN); 578c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_SUM); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci for (i = 0; i < 256; ++i) { 608c2ecf20Sopenharmony_ci vsp1_write(hgo->histo.entity.vsp1, 618c2ecf20Sopenharmony_ci VI6_HGO_EXT_HIST_ADDR, i); 628c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_EXT_HIST_DATA); 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci size = (2 + 256) * sizeof(u32); 668c2ecf20Sopenharmony_ci } else if (hgo->max_rgb) { 678c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_MAXMIN); 688c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_SUM); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci for (i = 0; i < 64; ++i) 718c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_HISTO(i)); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci size = (2 + 64) * sizeof(u32); 748c2ecf20Sopenharmony_ci } else { 758c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_R_MAXMIN); 768c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_MAXMIN); 778c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_B_MAXMIN); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_R_SUM); 808c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_SUM); 818c2ecf20Sopenharmony_ci *data++ = vsp1_hgo_read(hgo, VI6_HGO_B_SUM); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci for (i = 0; i < 64; ++i) { 848c2ecf20Sopenharmony_ci data[i] = vsp1_hgo_read(hgo, VI6_HGO_R_HISTO(i)); 858c2ecf20Sopenharmony_ci data[i+64] = vsp1_hgo_read(hgo, VI6_HGO_G_HISTO(i)); 868c2ecf20Sopenharmony_ci data[i+128] = vsp1_hgo_read(hgo, VI6_HGO_B_HISTO(i)); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci size = (6 + 64 * 3) * sizeof(u32); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci vsp1_histogram_buffer_complete(&hgo->histo, buf, size); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 968c2ecf20Sopenharmony_ci * Controls 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define V4L2_CID_VSP1_HGO_MAX_RGB (V4L2_CID_USER_BASE | 0x1001) 1008c2ecf20Sopenharmony_ci#define V4L2_CID_VSP1_HGO_NUM_BINS (V4L2_CID_USER_BASE | 0x1002) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config hgo_max_rgb_control = { 1038c2ecf20Sopenharmony_ci .id = V4L2_CID_VSP1_HGO_MAX_RGB, 1048c2ecf20Sopenharmony_ci .name = "Maximum RGB Mode", 1058c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 1068c2ecf20Sopenharmony_ci .min = 0, 1078c2ecf20Sopenharmony_ci .max = 1, 1088c2ecf20Sopenharmony_ci .def = 0, 1098c2ecf20Sopenharmony_ci .step = 1, 1108c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_MODIFY_LAYOUT, 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic const s64 hgo_num_bins[] = { 1148c2ecf20Sopenharmony_ci 64, 256, 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_config hgo_num_bins_control = { 1188c2ecf20Sopenharmony_ci .id = V4L2_CID_VSP1_HGO_NUM_BINS, 1198c2ecf20Sopenharmony_ci .name = "Number of Bins", 1208c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER_MENU, 1218c2ecf20Sopenharmony_ci .min = 0, 1228c2ecf20Sopenharmony_ci .max = 1, 1238c2ecf20Sopenharmony_ci .def = 0, 1248c2ecf20Sopenharmony_ci .qmenu_int = hgo_num_bins, 1258c2ecf20Sopenharmony_ci .flags = V4L2_CTRL_FLAG_MODIFY_LAYOUT, 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1298c2ecf20Sopenharmony_ci * VSP1 Entity Operations 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void hgo_configure_stream(struct vsp1_entity *entity, 1338c2ecf20Sopenharmony_ci struct vsp1_pipeline *pipe, 1348c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl, 1358c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct vsp1_hgo *hgo = to_hgo(&entity->subdev); 1388c2ecf20Sopenharmony_ci struct v4l2_rect *compose; 1398c2ecf20Sopenharmony_ci struct v4l2_rect *crop; 1408c2ecf20Sopenharmony_ci unsigned int hratio; 1418c2ecf20Sopenharmony_ci unsigned int vratio; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci crop = vsp1_entity_get_pad_selection(entity, entity->config, 1448c2ecf20Sopenharmony_ci HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); 1458c2ecf20Sopenharmony_ci compose = vsp1_entity_get_pad_selection(entity, entity->config, 1468c2ecf20Sopenharmony_ci HISTO_PAD_SINK, 1478c2ecf20Sopenharmony_ci V4L2_SEL_TGT_COMPOSE); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci vsp1_hgo_write(hgo, dlb, VI6_HGO_OFFSET, 1528c2ecf20Sopenharmony_ci (crop->left << VI6_HGO_OFFSET_HOFFSET_SHIFT) | 1538c2ecf20Sopenharmony_ci (crop->top << VI6_HGO_OFFSET_VOFFSET_SHIFT)); 1548c2ecf20Sopenharmony_ci vsp1_hgo_write(hgo, dlb, VI6_HGO_SIZE, 1558c2ecf20Sopenharmony_ci (crop->width << VI6_HGO_SIZE_HSIZE_SHIFT) | 1568c2ecf20Sopenharmony_ci (crop->height << VI6_HGO_SIZE_VSIZE_SHIFT)); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci mutex_lock(hgo->ctrls.handler.lock); 1598c2ecf20Sopenharmony_ci hgo->max_rgb = hgo->ctrls.max_rgb->cur.val; 1608c2ecf20Sopenharmony_ci if (hgo->ctrls.num_bins) 1618c2ecf20Sopenharmony_ci hgo->num_bins = hgo_num_bins[hgo->ctrls.num_bins->cur.val]; 1628c2ecf20Sopenharmony_ci mutex_unlock(hgo->ctrls.handler.lock); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci hratio = crop->width * 2 / compose->width / 3; 1658c2ecf20Sopenharmony_ci vratio = crop->height * 2 / compose->height / 3; 1668c2ecf20Sopenharmony_ci vsp1_hgo_write(hgo, dlb, VI6_HGO_MODE, 1678c2ecf20Sopenharmony_ci (hgo->num_bins == 256 ? VI6_HGO_MODE_STEP : 0) | 1688c2ecf20Sopenharmony_ci (hgo->max_rgb ? VI6_HGO_MODE_MAXRGB : 0) | 1698c2ecf20Sopenharmony_ci (hratio << VI6_HGO_MODE_HRATIO_SHIFT) | 1708c2ecf20Sopenharmony_ci (vratio << VI6_HGO_MODE_VRATIO_SHIFT)); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic const struct vsp1_entity_operations hgo_entity_ops = { 1748c2ecf20Sopenharmony_ci .configure_stream = hgo_configure_stream, 1758c2ecf20Sopenharmony_ci .destroy = vsp1_histogram_destroy, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1798c2ecf20Sopenharmony_ci * Initialization and Cleanup 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic const unsigned int hgo_mbus_formats[] = { 1838c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_AYUV8_1X32, 1848c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_ARGB8888_1X32, 1858c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_AHSV8888_1X32, 1868c2ecf20Sopenharmony_ci}; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistruct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct vsp1_hgo *hgo; 1918c2ecf20Sopenharmony_ci int ret; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci hgo = devm_kzalloc(vsp1->dev, sizeof(*hgo), GFP_KERNEL); 1948c2ecf20Sopenharmony_ci if (hgo == NULL) 1958c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Initialize the control handler. */ 1988c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&hgo->ctrls.handler, 1998c2ecf20Sopenharmony_ci vsp1->info->gen == 3 ? 2 : 1); 2008c2ecf20Sopenharmony_ci hgo->ctrls.max_rgb = v4l2_ctrl_new_custom(&hgo->ctrls.handler, 2018c2ecf20Sopenharmony_ci &hgo_max_rgb_control, NULL); 2028c2ecf20Sopenharmony_ci if (vsp1->info->gen == 3) 2038c2ecf20Sopenharmony_ci hgo->ctrls.num_bins = 2048c2ecf20Sopenharmony_ci v4l2_ctrl_new_custom(&hgo->ctrls.handler, 2058c2ecf20Sopenharmony_ci &hgo_num_bins_control, NULL); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci hgo->max_rgb = false; 2088c2ecf20Sopenharmony_ci hgo->num_bins = 64; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci hgo->histo.entity.subdev.ctrl_handler = &hgo->ctrls.handler; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Initialize the video device and queue for statistics data. */ 2138c2ecf20Sopenharmony_ci ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo", 2148c2ecf20Sopenharmony_ci &hgo_entity_ops, hgo_mbus_formats, 2158c2ecf20Sopenharmony_ci ARRAY_SIZE(hgo_mbus_formats), 2168c2ecf20Sopenharmony_ci HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO); 2178c2ecf20Sopenharmony_ci if (ret < 0) { 2188c2ecf20Sopenharmony_ci vsp1_entity_destroy(&hgo->histo.entity); 2198c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return hgo; 2238c2ecf20Sopenharmony_ci} 224