1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2026 Renesas Electronics Corp. 4 * Copyright (C) 2026 Ideas on Board Oy 5 * Copyright (C) 2026 Ragnatech AB 6 */ 7 8 #include "rpp_module.h" 9 10 #define LSC_VERSION_REG 0x0000 11 12 #define LSC_CTRL_REG 0x0004 13 #define LSC_CTRL_LSC_EN BIT(0) 14 15 #define LSC_R_TABLE_ADDR_REG 0x0008 16 #define LSC_GR_TABLE_ADDR_REG 0x000c 17 #define LSC_B_TABLE_ADDR_REG 0x0010 18 #define LSC_GB_TABLE_ADDR_REG 0x0014 19 #define LSC_R_TABLE_DATA_REG 0x0018 20 #define LSC_GR_TABLE_DATA_REG 0x001c 21 #define LSC_B_TABLE_DATA_REG 0x0020 22 #define LSC_GB_TABLE_DATA_REG 0x0024 23 #define LSC_XGRAD_01_REG 0x0028 24 #define LSC_XGRAD_23_REG 0x002c 25 #define LSC_XGRAD_45_REG 0x0030 26 #define LSC_XGRAD_67_REG 0x0034 27 #define LSC_XGRAD_89_REG 0x0038 28 #define LSC_XGRAD_1011_REG 0x003c 29 #define LSC_XGRAD_1213_REG 0x0040 30 #define LSC_XGRAD_1415_REG 0x0044 31 #define LSC_YGRAD_01_REG 0x0048 32 #define LSC_YGRAD_23_REG 0x004c 33 #define LSC_YGRAD_45_REG 0x0050 34 #define LSC_YGRAD_67_REG 0x0054 35 #define LSC_YGRAD_89_REG 0x0058 36 #define LSC_YGRAD_1011_REG 0x005c 37 #define LSC_YGRAD_1213_REG 0x0060 38 #define LSC_YGRAD_1415_REG 0x0064 39 #define LSC_XSIZE_01_REG 0x0068 40 #define LSC_XSIZE_23_REG 0x006c 41 #define LSC_XSIZE_45_REG 0x0070 42 #define LSC_XSIZE_67_REG 0x0074 43 #define LSC_XSIZE_89_REG 0x0078 44 #define LSC_XSIZE_1011_REG 0x007c 45 #define LSC_XSIZE_1213_REG 0x0080 46 #define LSC_XSIZE_1415_REG 0x0084 47 #define LSC_YSIZE_01_REG 0x0088 48 #define LSC_YSIZE_23_REG 0x008c 49 #define LSC_YSIZE_45_REG 0x0090 50 #define LSC_YSIZE_67_REG 0x0094 51 #define LSC_YSIZE_89_REG 0x0098 52 #define LSC_YSIZE_1011_REG 0x009c 53 #define LSC_YSIZE_1213_REG 0x00a0 54 #define LSC_YSIZE_1415_REG 0x00a4 55 #define LSC_TABLE_SEL_REG 0x00a8 56 #define LSC_STATUS_REG 0x00ac 57 58 #define LSC_R_TABLE_DATA_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 12)) 59 #define LSC_GRAD_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 16)) 60 #define LSC_SIZE_VALUE(v1, v2) (((v1) & 0x1ff) | (((v2) & 0x1ff) << 16)) 61 62 static int rppx1_lsc_probe(struct rpp_module *mod) 63 { 64 /* Version check. */ 65 if (rpp_module_read(mod, LSC_VERSION_REG) != 0x04) 66 return -EINVAL; 67 68 return 0; 69 } 70 71 static int 72 rppx1_lsc_fill_params(struct rpp_module *mod, 73 const union rppx1_params_block *block, 74 rppx1_reg_write write, void *priv) 75 { 76 const struct rppx1_lsc_params *cfg = &block->lsc; 77 const __u16 *v; 78 79 /* Always disable module as it needs be disabled before configuring. */ 80 write(priv, mod->base + LSC_CTRL_REG, 0); 81 if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) 82 return 0; 83 84 /* 85 * Program the color correction sectors. 86 * 87 * There are two tables to one can program and switch between. As the 88 * RPPX1 supports preparing a buffer of commands to be applied later 89 * only use table 0. This works as long as the ISP is not used in 90 * inline-mode. 91 * 92 * For inline-mode support using DMA for configuration is not possible 93 * so this is not an issue, but needs to be address if inline-mode 94 * support is added to the driver. 95 */ 96 97 /* Start writing at beginning of table 0. */ 98 write(priv, mod->base + LSC_R_TABLE_ADDR_REG, 0); 99 write(priv, mod->base + LSC_GR_TABLE_ADDR_REG, 0); 100 write(priv, mod->base + LSC_B_TABLE_ADDR_REG, 0); 101 write(priv, mod->base + LSC_GB_TABLE_ADDR_REG, 0); 102 103 /* Program data tables. */ 104 for (unsigned int i = 0; i < RPPX1_LSC_SAMPLES_MAX; i++) { 105 const __u16 *r = cfg->r_data[i]; 106 const __u16 *gr = cfg->gr_data[i]; 107 const __u16 *b = cfg->b_data[i]; 108 const __u16 *gb = cfg->gb_data[i]; 109 unsigned int j; 110 111 for (j = 0; j < RPPX1_LSC_SAMPLES_MAX - 1; j += 2) { 112 write(priv, mod->base + LSC_R_TABLE_DATA_REG, 113 LSC_R_TABLE_DATA_VALUE(r[j], r[j + 1])); 114 write(priv, mod->base + LSC_GR_TABLE_DATA_REG, 115 LSC_R_TABLE_DATA_VALUE(gr[j], gr[j + 1])); 116 write(priv, mod->base + LSC_B_TABLE_DATA_REG, 117 LSC_R_TABLE_DATA_VALUE(b[j], b[j + 1])); 118 write(priv, mod->base + LSC_GB_TABLE_DATA_REG, 119 LSC_R_TABLE_DATA_VALUE(gb[j], gb[j + 1])); 120 } 121 122 write(priv, mod->base + LSC_R_TABLE_DATA_REG, 123 LSC_R_TABLE_DATA_VALUE(r[j], 0)); 124 write(priv, mod->base + LSC_GR_TABLE_DATA_REG, 125 LSC_R_TABLE_DATA_VALUE(gr[j], 0)); 126 write(priv, mod->base + LSC_B_TABLE_DATA_REG, 127 LSC_R_TABLE_DATA_VALUE(b[j], 0)); 128 write(priv, mod->base + LSC_GB_TABLE_DATA_REG, 129 LSC_R_TABLE_DATA_VALUE(gb[j], 0)); 130 } 131 132 /* Activate table 0. */ 133 write(priv, mod->base + LSC_TABLE_SEL_REG, 0); 134 135 /* 136 * Program X- and Y- sizes, and gradients. 137 */ 138 139 v = cfg->x_grad; 140 write(priv, mod->base + LSC_XGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1])); 141 write(priv, mod->base + LSC_XGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3])); 142 write(priv, mod->base + LSC_XGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5])); 143 write(priv, mod->base + LSC_XGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7])); 144 write(priv, mod->base + LSC_XGRAD_89_REG, LSC_GRAD_VALUE(v[8], v[9])); 145 write(priv, mod->base + LSC_XGRAD_1011_REG, LSC_GRAD_VALUE(v[10], v[11])); 146 write(priv, mod->base + LSC_XGRAD_1213_REG, LSC_GRAD_VALUE(v[12], v[13])); 147 write(priv, mod->base + LSC_XGRAD_1415_REG, LSC_GRAD_VALUE(v[14], v[15])); 148 149 v = cfg->y_grad; 150 write(priv, mod->base + LSC_YGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1])); 151 write(priv, mod->base + LSC_YGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3])); 152 write(priv, mod->base + LSC_YGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5])); 153 write(priv, mod->base + LSC_YGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7])); 154 write(priv, mod->base + LSC_YGRAD_89_REG, LSC_GRAD_VALUE(v[8], v[9])); 155 write(priv, mod->base + LSC_YGRAD_1011_REG, LSC_GRAD_VALUE(v[10], v[11])); 156 write(priv, mod->base + LSC_YGRAD_1213_REG, LSC_GRAD_VALUE(v[12], v[13])); 157 write(priv, mod->base + LSC_YGRAD_1415_REG, LSC_GRAD_VALUE(v[14], v[15])); 158 159 v = cfg->x_sect_size; 160 write(priv, mod->base + LSC_XSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1])); 161 write(priv, mod->base + LSC_XSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3])); 162 write(priv, mod->base + LSC_XSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5])); 163 write(priv, mod->base + LSC_XSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7])); 164 write(priv, mod->base + LSC_XSIZE_89_REG, LSC_GRAD_VALUE(v[8], v[9])); 165 write(priv, mod->base + LSC_XSIZE_1011_REG, LSC_GRAD_VALUE(v[10], v[11])); 166 write(priv, mod->base + LSC_XSIZE_1213_REG, LSC_GRAD_VALUE(v[12], v[13])); 167 write(priv, mod->base + LSC_XSIZE_1415_REG, LSC_GRAD_VALUE(v[14], v[15])); 168 169 v = cfg->y_sect_size; 170 write(priv, mod->base + LSC_YSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1])); 171 write(priv, mod->base + LSC_YSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3])); 172 write(priv, mod->base + LSC_YSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5])); 173 write(priv, mod->base + LSC_YSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7])); 174 write(priv, mod->base + LSC_YSIZE_89_REG, LSC_GRAD_VALUE(v[8], v[9])); 175 write(priv, mod->base + LSC_YSIZE_1011_REG, LSC_GRAD_VALUE(v[10], v[11])); 176 write(priv, mod->base + LSC_YSIZE_1213_REG, LSC_GRAD_VALUE(v[12], v[13])); 177 write(priv, mod->base + LSC_YSIZE_1415_REG, LSC_GRAD_VALUE(v[14], v[15])); 178 179 /* Enable module. */ 180 write(priv, mod->base + LSC_CTRL_REG, LSC_CTRL_LSC_EN); 181 182 return 0; 183 } 184 185 const struct rpp_module_ops rppx1_lsc_ops = { 186 .probe = rppx1_lsc_probe, 187 .fill_params = rppx1_lsc_fill_params, 188 }; 189