1dacca5f0SHans Verkuil // SPDX-License-Identifier: GPL-2.0-or-later 2dacca5f0SHans Verkuil /* 3dacca5f0SHans Verkuil * vimc-debayer.c Virtual Media Controller Driver 4dacca5f0SHans Verkuil * 5dacca5f0SHans Verkuil * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> 6dacca5f0SHans Verkuil */ 7dacca5f0SHans Verkuil 8dacca5f0SHans Verkuil #include <linux/moduleparam.h> 9dacca5f0SHans Verkuil #include <linux/platform_device.h> 10dacca5f0SHans Verkuil #include <linux/vmalloc.h> 11dacca5f0SHans Verkuil #include <linux/v4l2-mediabus.h> 12dacca5f0SHans Verkuil #include <media/v4l2-ctrls.h> 13dacca5f0SHans Verkuil #include <media/v4l2-event.h> 14dacca5f0SHans Verkuil #include <media/v4l2-subdev.h> 15dacca5f0SHans Verkuil 16dacca5f0SHans Verkuil #include "vimc-common.h" 17dacca5f0SHans Verkuil 18ec917d77SDaniel Oakley enum vimc_debayer_rgb_colors { 19ec917d77SDaniel Oakley VIMC_DEBAYER_RED = 0, 20ec917d77SDaniel Oakley VIMC_DEBAYER_GREEN = 1, 21ec917d77SDaniel Oakley VIMC_DEBAYER_BLUE = 2, 22dacca5f0SHans Verkuil }; 23dacca5f0SHans Verkuil 24ec917d77SDaniel Oakley struct vimc_debayer_pix_map { 25dacca5f0SHans Verkuil u32 code; 26ec917d77SDaniel Oakley enum vimc_debayer_rgb_colors order[2][2]; 27dacca5f0SHans Verkuil }; 28dacca5f0SHans Verkuil 29ec917d77SDaniel Oakley struct vimc_debayer_device { 30dacca5f0SHans Verkuil struct vimc_ent_device ved; 31dacca5f0SHans Verkuil struct v4l2_subdev sd; 32dacca5f0SHans Verkuil /* The active format */ 33dacca5f0SHans Verkuil struct v4l2_mbus_framefmt sink_fmt; 34dacca5f0SHans Verkuil u32 src_code; 35ec917d77SDaniel Oakley void (*set_rgb_src)(struct vimc_debayer_device *vdebayer, 36ec917d77SDaniel Oakley unsigned int lin, unsigned int col, 37ec917d77SDaniel Oakley unsigned int rgb[3]); 38dacca5f0SHans Verkuil /* Values calculated when the stream starts */ 39dacca5f0SHans Verkuil u8 *src_frame; 40ec917d77SDaniel Oakley const struct vimc_debayer_pix_map *sink_pix_map; 41dacca5f0SHans Verkuil unsigned int sink_bpp; 42dacca5f0SHans Verkuil unsigned int mean_win_size; 43dacca5f0SHans Verkuil struct v4l2_ctrl_handler hdl; 44dacca5f0SHans Verkuil struct media_pad pads[2]; 45dacca5f0SHans Verkuil }; 46dacca5f0SHans Verkuil 47dacca5f0SHans Verkuil static const struct v4l2_mbus_framefmt sink_fmt_default = { 48dacca5f0SHans Verkuil .width = 640, 49dacca5f0SHans Verkuil .height = 480, 50dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SRGGB8_1X8, 51dacca5f0SHans Verkuil .field = V4L2_FIELD_NONE, 529a42a5ffSNiklas Söderlund .colorspace = V4L2_COLORSPACE_SRGB, 53dacca5f0SHans Verkuil }; 54dacca5f0SHans Verkuil 55ec917d77SDaniel Oakley static const u32 vimc_debayer_src_mbus_codes[] = { 56f4d12d80SNícolas F. R. A. Prado MEDIA_BUS_FMT_GBR888_1X24, 57f4d12d80SNícolas F. R. A. Prado MEDIA_BUS_FMT_BGR888_1X24, 58f4d12d80SNícolas F. R. A. Prado MEDIA_BUS_FMT_BGR888_3X8, 59f4d12d80SNícolas F. R. A. Prado MEDIA_BUS_FMT_RGB888_1X24, 60f4d12d80SNícolas F. R. A. Prado MEDIA_BUS_FMT_RGB888_2X12_BE, 61f4d12d80SNícolas F. R. A. Prado MEDIA_BUS_FMT_RGB888_2X12_LE, 62f4d12d80SNícolas F. R. A. Prado MEDIA_BUS_FMT_RGB888_3X8, 63f4d12d80SNícolas F. R. A. Prado MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, 64f4d12d80SNícolas F. R. A. Prado MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, 65f4d12d80SNícolas F. R. A. Prado MEDIA_BUS_FMT_RGB888_1X32_PADHI, 66f4d12d80SNícolas F. R. A. Prado }; 67f4d12d80SNícolas F. R. A. Prado 68ec917d77SDaniel Oakley static const struct vimc_debayer_pix_map vimc_debayer_pix_map_list[] = { 69dacca5f0SHans Verkuil { 70dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SBGGR8_1X8, 71ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN }, 72ec917d77SDaniel Oakley { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED } } 73dacca5f0SHans Verkuil }, 74dacca5f0SHans Verkuil { 75dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SGBRG8_1X8, 76ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE }, 77ec917d77SDaniel Oakley { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN } } 78dacca5f0SHans Verkuil }, 79dacca5f0SHans Verkuil { 80dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SGRBG8_1X8, 81ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED }, 82ec917d77SDaniel Oakley { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN } } 83dacca5f0SHans Verkuil }, 84dacca5f0SHans Verkuil { 85dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SRGGB8_1X8, 86ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN }, 87ec917d77SDaniel Oakley { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE } } 88dacca5f0SHans Verkuil }, 89dacca5f0SHans Verkuil { 90dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SBGGR10_1X10, 91ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN }, 92ec917d77SDaniel Oakley { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED } } 93dacca5f0SHans Verkuil }, 94dacca5f0SHans Verkuil { 95dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SGBRG10_1X10, 96ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE }, 97ec917d77SDaniel Oakley { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN } } 98dacca5f0SHans Verkuil }, 99dacca5f0SHans Verkuil { 100dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SGRBG10_1X10, 101ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED }, 102ec917d77SDaniel Oakley { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN } } 103dacca5f0SHans Verkuil }, 104dacca5f0SHans Verkuil { 105dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SRGGB10_1X10, 106ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN }, 107ec917d77SDaniel Oakley { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE } } 108dacca5f0SHans Verkuil }, 109dacca5f0SHans Verkuil { 110dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SBGGR12_1X12, 111ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN }, 112ec917d77SDaniel Oakley { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED } } 113dacca5f0SHans Verkuil }, 114dacca5f0SHans Verkuil { 115dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SGBRG12_1X12, 116ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE }, 117ec917d77SDaniel Oakley { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN } } 118dacca5f0SHans Verkuil }, 119dacca5f0SHans Verkuil { 120dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SGRBG12_1X12, 121ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_RED }, 122ec917d77SDaniel Oakley { VIMC_DEBAYER_BLUE, VIMC_DEBAYER_GREEN } } 123dacca5f0SHans Verkuil }, 124dacca5f0SHans Verkuil { 125dacca5f0SHans Verkuil .code = MEDIA_BUS_FMT_SRGGB12_1X12, 126ec917d77SDaniel Oakley .order = { { VIMC_DEBAYER_RED, VIMC_DEBAYER_GREEN }, 127ec917d77SDaniel Oakley { VIMC_DEBAYER_GREEN, VIMC_DEBAYER_BLUE } } 128dacca5f0SHans Verkuil }, 129dacca5f0SHans Verkuil }; 130dacca5f0SHans Verkuil 131ec917d77SDaniel Oakley static const struct vimc_debayer_pix_map *vimc_debayer_pix_map_by_code(u32 code) 132dacca5f0SHans Verkuil { 133dacca5f0SHans Verkuil unsigned int i; 134dacca5f0SHans Verkuil 135ec917d77SDaniel Oakley for (i = 0; i < ARRAY_SIZE(vimc_debayer_pix_map_list); i++) 136ec917d77SDaniel Oakley if (vimc_debayer_pix_map_list[i].code == code) 137ec917d77SDaniel Oakley return &vimc_debayer_pix_map_list[i]; 138dacca5f0SHans Verkuil 139dacca5f0SHans Verkuil return NULL; 140dacca5f0SHans Verkuil } 141dacca5f0SHans Verkuil 142ec917d77SDaniel Oakley static bool vimc_debayer_src_code_is_valid(u32 code) 143f4d12d80SNícolas F. R. A. Prado { 144f4d12d80SNícolas F. R. A. Prado unsigned int i; 145f4d12d80SNícolas F. R. A. Prado 146ec917d77SDaniel Oakley for (i = 0; i < ARRAY_SIZE(vimc_debayer_src_mbus_codes); i++) 147ec917d77SDaniel Oakley if (vimc_debayer_src_mbus_codes[i] == code) 148f4d12d80SNícolas F. R. A. Prado return true; 149f4d12d80SNícolas F. R. A. Prado 150f4d12d80SNícolas F. R. A. Prado return false; 151f4d12d80SNícolas F. R. A. Prado } 152f4d12d80SNícolas F. R. A. Prado 153ec917d77SDaniel Oakley static int vimc_debayer_init_cfg(struct v4l2_subdev *sd, 1540d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state) 155dacca5f0SHans Verkuil { 156ec917d77SDaniel Oakley struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); 157dacca5f0SHans Verkuil struct v4l2_mbus_framefmt *mf; 158dacca5f0SHans Verkuil unsigned int i; 159dacca5f0SHans Verkuil 160*bc0e8d91SSakari Ailus mf = v4l2_subdev_state_get_format(sd_state, 0); 161dacca5f0SHans Verkuil *mf = sink_fmt_default; 162dacca5f0SHans Verkuil 163dacca5f0SHans Verkuil for (i = 1; i < sd->entity.num_pads; i++) { 164*bc0e8d91SSakari Ailus mf = v4l2_subdev_state_get_format(sd_state, i); 165dacca5f0SHans Verkuil *mf = sink_fmt_default; 166ec917d77SDaniel Oakley mf->code = vdebayer->src_code; 167dacca5f0SHans Verkuil } 168dacca5f0SHans Verkuil 169dacca5f0SHans Verkuil return 0; 170dacca5f0SHans Verkuil } 171dacca5f0SHans Verkuil 172ec917d77SDaniel Oakley static int vimc_debayer_enum_mbus_code(struct v4l2_subdev *sd, 1730d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 174dacca5f0SHans Verkuil struct v4l2_subdev_mbus_code_enum *code) 175dacca5f0SHans Verkuil { 176dacca5f0SHans Verkuil if (VIMC_IS_SRC(code->pad)) { 177ec917d77SDaniel Oakley if (code->index >= ARRAY_SIZE(vimc_debayer_src_mbus_codes)) 178dacca5f0SHans Verkuil return -EINVAL; 179dacca5f0SHans Verkuil 180ec917d77SDaniel Oakley code->code = vimc_debayer_src_mbus_codes[code->index]; 181dacca5f0SHans Verkuil } else { 182ec917d77SDaniel Oakley if (code->index >= ARRAY_SIZE(vimc_debayer_pix_map_list)) 183dacca5f0SHans Verkuil return -EINVAL; 184dacca5f0SHans Verkuil 185ec917d77SDaniel Oakley code->code = vimc_debayer_pix_map_list[code->index].code; 186dacca5f0SHans Verkuil } 187dacca5f0SHans Verkuil 188dacca5f0SHans Verkuil return 0; 189dacca5f0SHans Verkuil } 190dacca5f0SHans Verkuil 191ec917d77SDaniel Oakley static int vimc_debayer_enum_frame_size(struct v4l2_subdev *sd, 1920d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 193dacca5f0SHans Verkuil struct v4l2_subdev_frame_size_enum *fse) 194dacca5f0SHans Verkuil { 195dacca5f0SHans Verkuil if (fse->index) 196dacca5f0SHans Verkuil return -EINVAL; 197dacca5f0SHans Verkuil 198dacca5f0SHans Verkuil if (VIMC_IS_SINK(fse->pad)) { 199ec917d77SDaniel Oakley const struct vimc_debayer_pix_map *vpix = 200ec917d77SDaniel Oakley vimc_debayer_pix_map_by_code(fse->code); 201dacca5f0SHans Verkuil 202dacca5f0SHans Verkuil if (!vpix) 203dacca5f0SHans Verkuil return -EINVAL; 204ec917d77SDaniel Oakley } else if (!vimc_debayer_src_code_is_valid(fse->code)) { 205dacca5f0SHans Verkuil return -EINVAL; 206dacca5f0SHans Verkuil } 207dacca5f0SHans Verkuil 208dacca5f0SHans Verkuil fse->min_width = VIMC_FRAME_MIN_WIDTH; 209dacca5f0SHans Verkuil fse->max_width = VIMC_FRAME_MAX_WIDTH; 210dacca5f0SHans Verkuil fse->min_height = VIMC_FRAME_MIN_HEIGHT; 211dacca5f0SHans Verkuil fse->max_height = VIMC_FRAME_MAX_HEIGHT; 212dacca5f0SHans Verkuil 213dacca5f0SHans Verkuil return 0; 214dacca5f0SHans Verkuil } 215dacca5f0SHans Verkuil 216ec917d77SDaniel Oakley static int vimc_debayer_get_fmt(struct v4l2_subdev *sd, 2170d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 218dacca5f0SHans Verkuil struct v4l2_subdev_format *fmt) 219dacca5f0SHans Verkuil { 220ec917d77SDaniel Oakley struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); 221dacca5f0SHans Verkuil 222dacca5f0SHans Verkuil /* Get the current sink format */ 223dacca5f0SHans Verkuil fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? 224*bc0e8d91SSakari Ailus *v4l2_subdev_state_get_format(sd_state, 0) : 225ec917d77SDaniel Oakley vdebayer->sink_fmt; 226dacca5f0SHans Verkuil 227dacca5f0SHans Verkuil /* Set the right code for the source pad */ 228dacca5f0SHans Verkuil if (VIMC_IS_SRC(fmt->pad)) 229ec917d77SDaniel Oakley fmt->format.code = vdebayer->src_code; 230dacca5f0SHans Verkuil 231dacca5f0SHans Verkuil return 0; 232dacca5f0SHans Verkuil } 233dacca5f0SHans Verkuil 234ec917d77SDaniel Oakley static void vimc_debayer_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt) 235dacca5f0SHans Verkuil { 236ec917d77SDaniel Oakley const struct vimc_debayer_pix_map *vpix; 237dacca5f0SHans Verkuil 238dacca5f0SHans Verkuil /* Don't accept a code that is not on the debayer table */ 239ec917d77SDaniel Oakley vpix = vimc_debayer_pix_map_by_code(fmt->code); 240dacca5f0SHans Verkuil if (!vpix) 241dacca5f0SHans Verkuil fmt->code = sink_fmt_default.code; 242dacca5f0SHans Verkuil 243dacca5f0SHans Verkuil fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH, 244dacca5f0SHans Verkuil VIMC_FRAME_MAX_WIDTH) & ~1; 245dacca5f0SHans Verkuil fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT, 246dacca5f0SHans Verkuil VIMC_FRAME_MAX_HEIGHT) & ~1; 247dacca5f0SHans Verkuil 248dacca5f0SHans Verkuil if (fmt->field == V4L2_FIELD_ANY) 249dacca5f0SHans Verkuil fmt->field = sink_fmt_default.field; 250dacca5f0SHans Verkuil 251dacca5f0SHans Verkuil vimc_colorimetry_clamp(fmt); 252dacca5f0SHans Verkuil } 253dacca5f0SHans Verkuil 254ec917d77SDaniel Oakley static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, 2550d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 256dacca5f0SHans Verkuil struct v4l2_subdev_format *fmt) 257dacca5f0SHans Verkuil { 258ec917d77SDaniel Oakley struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); 259dacca5f0SHans Verkuil struct v4l2_mbus_framefmt *sink_fmt; 260f4d12d80SNícolas F. R. A. Prado u32 *src_code; 261dacca5f0SHans Verkuil 262dacca5f0SHans Verkuil if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 263dacca5f0SHans Verkuil /* Do not change the format while stream is on */ 264ec917d77SDaniel Oakley if (vdebayer->src_frame) 265dacca5f0SHans Verkuil return -EBUSY; 266dacca5f0SHans Verkuil 267ec917d77SDaniel Oakley sink_fmt = &vdebayer->sink_fmt; 268ec917d77SDaniel Oakley src_code = &vdebayer->src_code; 269dacca5f0SHans Verkuil } else { 270*bc0e8d91SSakari Ailus sink_fmt = v4l2_subdev_state_get_format(sd_state, 0); 271*bc0e8d91SSakari Ailus src_code = &v4l2_subdev_state_get_format(sd_state, 1)->code; 272dacca5f0SHans Verkuil } 273dacca5f0SHans Verkuil 274dacca5f0SHans Verkuil /* 275dacca5f0SHans Verkuil * Do not change the format of the source pad, 276dacca5f0SHans Verkuil * it is propagated from the sink 277dacca5f0SHans Verkuil */ 278dacca5f0SHans Verkuil if (VIMC_IS_SRC(fmt->pad)) { 279f4d12d80SNícolas F. R. A. Prado u32 code = fmt->format.code; 280f4d12d80SNícolas F. R. A. Prado 281dacca5f0SHans Verkuil fmt->format = *sink_fmt; 282f4d12d80SNícolas F. R. A. Prado 283ec917d77SDaniel Oakley if (vimc_debayer_src_code_is_valid(code)) 284f4d12d80SNícolas F. R. A. Prado *src_code = code; 285f4d12d80SNícolas F. R. A. Prado 286f4d12d80SNícolas F. R. A. Prado fmt->format.code = *src_code; 287dacca5f0SHans Verkuil } else { 288dacca5f0SHans Verkuil /* Set the new format in the sink pad */ 289ec917d77SDaniel Oakley vimc_debayer_adjust_sink_fmt(&fmt->format); 290dacca5f0SHans Verkuil 291ec917d77SDaniel Oakley dev_dbg(vdebayer->ved.dev, "%s: sink format update: " 292dacca5f0SHans Verkuil "old:%dx%d (0x%x, %d, %d, %d, %d) " 293ec917d77SDaniel Oakley "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdebayer->sd.name, 294dacca5f0SHans Verkuil /* old */ 295dacca5f0SHans Verkuil sink_fmt->width, sink_fmt->height, sink_fmt->code, 296dacca5f0SHans Verkuil sink_fmt->colorspace, sink_fmt->quantization, 297dacca5f0SHans Verkuil sink_fmt->xfer_func, sink_fmt->ycbcr_enc, 298dacca5f0SHans Verkuil /* new */ 299dacca5f0SHans Verkuil fmt->format.width, fmt->format.height, fmt->format.code, 300dacca5f0SHans Verkuil fmt->format.colorspace, fmt->format.quantization, 301dacca5f0SHans Verkuil fmt->format.xfer_func, fmt->format.ycbcr_enc); 302dacca5f0SHans Verkuil 303dacca5f0SHans Verkuil *sink_fmt = fmt->format; 304dacca5f0SHans Verkuil } 305dacca5f0SHans Verkuil 306dacca5f0SHans Verkuil return 0; 307dacca5f0SHans Verkuil } 308dacca5f0SHans Verkuil 309ec917d77SDaniel Oakley static const struct v4l2_subdev_pad_ops vimc_debayer_pad_ops = { 310ec917d77SDaniel Oakley .init_cfg = vimc_debayer_init_cfg, 311ec917d77SDaniel Oakley .enum_mbus_code = vimc_debayer_enum_mbus_code, 312ec917d77SDaniel Oakley .enum_frame_size = vimc_debayer_enum_frame_size, 313ec917d77SDaniel Oakley .get_fmt = vimc_debayer_get_fmt, 314ec917d77SDaniel Oakley .set_fmt = vimc_debayer_set_fmt, 315dacca5f0SHans Verkuil }; 316dacca5f0SHans Verkuil 317ec917d77SDaniel Oakley static void vimc_debayer_process_rgb_frame(struct vimc_debayer_device *vdebayer, 318dacca5f0SHans Verkuil unsigned int lin, 319dacca5f0SHans Verkuil unsigned int col, 320dacca5f0SHans Verkuil unsigned int rgb[3]) 321dacca5f0SHans Verkuil { 322f4d12d80SNícolas F. R. A. Prado const struct vimc_pix_map *vpix; 323dacca5f0SHans Verkuil unsigned int i, index; 324dacca5f0SHans Verkuil 325ec917d77SDaniel Oakley vpix = vimc_pix_map_by_code(vdebayer->src_code); 326ec917d77SDaniel Oakley index = VIMC_FRAME_INDEX(lin, col, vdebayer->sink_fmt.width, 3); 327f4d12d80SNícolas F. R. A. Prado for (i = 0; i < 3; i++) { 328f4d12d80SNícolas F. R. A. Prado switch (vpix->pixelformat) { 329f4d12d80SNícolas F. R. A. Prado case V4L2_PIX_FMT_RGB24: 330ec917d77SDaniel Oakley vdebayer->src_frame[index + i] = rgb[i]; 331f4d12d80SNícolas F. R. A. Prado break; 332f4d12d80SNícolas F. R. A. Prado case V4L2_PIX_FMT_BGR24: 333ec917d77SDaniel Oakley vdebayer->src_frame[index + i] = rgb[2 - i]; 334f4d12d80SNícolas F. R. A. Prado break; 335f4d12d80SNícolas F. R. A. Prado } 336f4d12d80SNícolas F. R. A. Prado } 337dacca5f0SHans Verkuil } 338dacca5f0SHans Verkuil 339ec917d77SDaniel Oakley static int vimc_debayer_s_stream(struct v4l2_subdev *sd, int enable) 340dacca5f0SHans Verkuil { 341ec917d77SDaniel Oakley struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); 342dacca5f0SHans Verkuil 343dacca5f0SHans Verkuil if (enable) { 344dacca5f0SHans Verkuil const struct vimc_pix_map *vpix; 345dacca5f0SHans Verkuil unsigned int frame_size; 346dacca5f0SHans Verkuil 347ec917d77SDaniel Oakley if (vdebayer->src_frame) 348dacca5f0SHans Verkuil return 0; 349dacca5f0SHans Verkuil 350dacca5f0SHans Verkuil /* Calculate the frame size of the source pad */ 351ec917d77SDaniel Oakley vpix = vimc_pix_map_by_code(vdebayer->src_code); 352ec917d77SDaniel Oakley frame_size = vdebayer->sink_fmt.width * vdebayer->sink_fmt.height * 353dacca5f0SHans Verkuil vpix->bpp; 354dacca5f0SHans Verkuil 355dacca5f0SHans Verkuil /* Save the bytes per pixel of the sink */ 356ec917d77SDaniel Oakley vpix = vimc_pix_map_by_code(vdebayer->sink_fmt.code); 357ec917d77SDaniel Oakley vdebayer->sink_bpp = vpix->bpp; 358dacca5f0SHans Verkuil 359dacca5f0SHans Verkuil /* Get the corresponding pixel map from the table */ 360ec917d77SDaniel Oakley vdebayer->sink_pix_map = 361ec917d77SDaniel Oakley vimc_debayer_pix_map_by_code(vdebayer->sink_fmt.code); 362dacca5f0SHans Verkuil 363dacca5f0SHans Verkuil /* 364dacca5f0SHans Verkuil * Allocate the frame buffer. Use vmalloc to be able to 365dacca5f0SHans Verkuil * allocate a large amount of memory 366dacca5f0SHans Verkuil */ 367ec917d77SDaniel Oakley vdebayer->src_frame = vmalloc(frame_size); 368ec917d77SDaniel Oakley if (!vdebayer->src_frame) 369dacca5f0SHans Verkuil return -ENOMEM; 370dacca5f0SHans Verkuil 371dacca5f0SHans Verkuil } else { 372ec917d77SDaniel Oakley if (!vdebayer->src_frame) 373dacca5f0SHans Verkuil return 0; 374dacca5f0SHans Verkuil 375ec917d77SDaniel Oakley vfree(vdebayer->src_frame); 376ec917d77SDaniel Oakley vdebayer->src_frame = NULL; 377dacca5f0SHans Verkuil } 378dacca5f0SHans Verkuil 379dacca5f0SHans Verkuil return 0; 380dacca5f0SHans Verkuil } 381dacca5f0SHans Verkuil 382ec917d77SDaniel Oakley static const struct v4l2_subdev_core_ops vimc_debayer_core_ops = { 383dacca5f0SHans Verkuil .log_status = v4l2_ctrl_subdev_log_status, 384dacca5f0SHans Verkuil .subscribe_event = v4l2_ctrl_subdev_subscribe_event, 385dacca5f0SHans Verkuil .unsubscribe_event = v4l2_event_subdev_unsubscribe, 386dacca5f0SHans Verkuil }; 387dacca5f0SHans Verkuil 388ec917d77SDaniel Oakley static const struct v4l2_subdev_video_ops vimc_debayer_video_ops = { 389ec917d77SDaniel Oakley .s_stream = vimc_debayer_s_stream, 390dacca5f0SHans Verkuil }; 391dacca5f0SHans Verkuil 392ec917d77SDaniel Oakley static const struct v4l2_subdev_ops vimc_debayer_ops = { 393ec917d77SDaniel Oakley .core = &vimc_debayer_core_ops, 394ec917d77SDaniel Oakley .pad = &vimc_debayer_pad_ops, 395ec917d77SDaniel Oakley .video = &vimc_debayer_video_ops, 396dacca5f0SHans Verkuil }; 397dacca5f0SHans Verkuil 398ec917d77SDaniel Oakley static unsigned int vimc_debayer_get_val(const u8 *bytes, 399dacca5f0SHans Verkuil const unsigned int n_bytes) 400dacca5f0SHans Verkuil { 401dacca5f0SHans Verkuil unsigned int i; 402dacca5f0SHans Verkuil unsigned int acc = 0; 403dacca5f0SHans Verkuil 404dacca5f0SHans Verkuil for (i = 0; i < n_bytes; i++) 405dacca5f0SHans Verkuil acc = acc + (bytes[i] << (8 * i)); 406dacca5f0SHans Verkuil 407dacca5f0SHans Verkuil return acc; 408dacca5f0SHans Verkuil } 409dacca5f0SHans Verkuil 410ec917d77SDaniel Oakley static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, 411dacca5f0SHans Verkuil const u8 *frame, 412dacca5f0SHans Verkuil const unsigned int lin, 413dacca5f0SHans Verkuil const unsigned int col, 414dacca5f0SHans Verkuil unsigned int rgb[3]) 415dacca5f0SHans Verkuil { 416dacca5f0SHans Verkuil unsigned int i, seek, wlin, wcol; 417dacca5f0SHans Verkuil unsigned int n_rgb[3] = {0, 0, 0}; 418dacca5f0SHans Verkuil 419dacca5f0SHans Verkuil for (i = 0; i < 3; i++) 420dacca5f0SHans Verkuil rgb[i] = 0; 421dacca5f0SHans Verkuil 422dacca5f0SHans Verkuil /* 423dacca5f0SHans Verkuil * Calculate how many we need to subtract to get to the pixel in 424dacca5f0SHans Verkuil * the top left corner of the mean window (considering the current 425dacca5f0SHans Verkuil * pixel as the center) 426dacca5f0SHans Verkuil */ 427ec917d77SDaniel Oakley seek = vdebayer->mean_win_size / 2; 428dacca5f0SHans Verkuil 429dacca5f0SHans Verkuil /* Sum the values of the colors in the mean window */ 430dacca5f0SHans Verkuil 431ec917d77SDaniel Oakley dev_dbg(vdebayer->ved.dev, 432dacca5f0SHans Verkuil "deb: %s: --- Calc pixel %dx%d, window mean %d, seek %d ---\n", 433ec917d77SDaniel Oakley vdebayer->sd.name, lin, col, vdebayer->sink_fmt.height, seek); 434dacca5f0SHans Verkuil 435dacca5f0SHans Verkuil /* 436dacca5f0SHans Verkuil * Iterate through all the lines in the mean window, start 437dacca5f0SHans Verkuil * with zero if the pixel is outside the frame and don't pass 438dacca5f0SHans Verkuil * the height when the pixel is in the bottom border of the 439dacca5f0SHans Verkuil * frame 440dacca5f0SHans Verkuil */ 441dacca5f0SHans Verkuil for (wlin = seek > lin ? 0 : lin - seek; 442ec917d77SDaniel Oakley wlin < lin + seek + 1 && wlin < vdebayer->sink_fmt.height; 443dacca5f0SHans Verkuil wlin++) { 444dacca5f0SHans Verkuil 445dacca5f0SHans Verkuil /* 446dacca5f0SHans Verkuil * Iterate through all the columns in the mean window, start 447dacca5f0SHans Verkuil * with zero if the pixel is outside the frame and don't pass 448dacca5f0SHans Verkuil * the width when the pixel is in the right border of the 449dacca5f0SHans Verkuil * frame 450dacca5f0SHans Verkuil */ 451dacca5f0SHans Verkuil for (wcol = seek > col ? 0 : col - seek; 452ec917d77SDaniel Oakley wcol < col + seek + 1 && wcol < vdebayer->sink_fmt.width; 453dacca5f0SHans Verkuil wcol++) { 454ec917d77SDaniel Oakley enum vimc_debayer_rgb_colors color; 455dacca5f0SHans Verkuil unsigned int index; 456dacca5f0SHans Verkuil 457dacca5f0SHans Verkuil /* Check which color this pixel is */ 458ec917d77SDaniel Oakley color = vdebayer->sink_pix_map->order[wlin % 2][wcol % 2]; 459dacca5f0SHans Verkuil 460dacca5f0SHans Verkuil index = VIMC_FRAME_INDEX(wlin, wcol, 461ec917d77SDaniel Oakley vdebayer->sink_fmt.width, 462ec917d77SDaniel Oakley vdebayer->sink_bpp); 463dacca5f0SHans Verkuil 464ec917d77SDaniel Oakley dev_dbg(vdebayer->ved.dev, 465dacca5f0SHans Verkuil "deb: %s: RGB CALC: frame index %d, win pos %dx%d, color %d\n", 466ec917d77SDaniel Oakley vdebayer->sd.name, index, wlin, wcol, color); 467dacca5f0SHans Verkuil 468dacca5f0SHans Verkuil /* Get its value */ 469dacca5f0SHans Verkuil rgb[color] = rgb[color] + 470ec917d77SDaniel Oakley vimc_debayer_get_val(&frame[index], 471ec917d77SDaniel Oakley vdebayer->sink_bpp); 472dacca5f0SHans Verkuil 473dacca5f0SHans Verkuil /* Save how many values we already added */ 474dacca5f0SHans Verkuil n_rgb[color]++; 475dacca5f0SHans Verkuil 476ec917d77SDaniel Oakley dev_dbg(vdebayer->ved.dev, "deb: %s: RGB CALC: val %d, n %d\n", 477ec917d77SDaniel Oakley vdebayer->sd.name, rgb[color], n_rgb[color]); 478dacca5f0SHans Verkuil } 479dacca5f0SHans Verkuil } 480dacca5f0SHans Verkuil 481dacca5f0SHans Verkuil /* Calculate the mean */ 482dacca5f0SHans Verkuil for (i = 0; i < 3; i++) { 483ec917d77SDaniel Oakley dev_dbg(vdebayer->ved.dev, 484dacca5f0SHans Verkuil "deb: %s: PRE CALC: %dx%d Color %d, val %d, n %d\n", 485ec917d77SDaniel Oakley vdebayer->sd.name, lin, col, i, rgb[i], n_rgb[i]); 486dacca5f0SHans Verkuil 487dacca5f0SHans Verkuil if (n_rgb[i]) 488dacca5f0SHans Verkuil rgb[i] = rgb[i] / n_rgb[i]; 489dacca5f0SHans Verkuil 490ec917d77SDaniel Oakley dev_dbg(vdebayer->ved.dev, 491dacca5f0SHans Verkuil "deb: %s: FINAL CALC: %dx%d Color %d, val %d\n", 492ec917d77SDaniel Oakley vdebayer->sd.name, lin, col, i, rgb[i]); 493dacca5f0SHans Verkuil } 494dacca5f0SHans Verkuil } 495dacca5f0SHans Verkuil 496ec917d77SDaniel Oakley static void *vimc_debayer_process_frame(struct vimc_ent_device *ved, 497dacca5f0SHans Verkuil const void *sink_frame) 498dacca5f0SHans Verkuil { 499ec917d77SDaniel Oakley struct vimc_debayer_device *vdebayer = 500ec917d77SDaniel Oakley container_of(ved, struct vimc_debayer_device, ved); 501ec917d77SDaniel Oakley 502dacca5f0SHans Verkuil unsigned int rgb[3]; 503dacca5f0SHans Verkuil unsigned int i, j; 504dacca5f0SHans Verkuil 505dacca5f0SHans Verkuil /* If the stream in this node is not active, just return */ 506ec917d77SDaniel Oakley if (!vdebayer->src_frame) 507dacca5f0SHans Verkuil return ERR_PTR(-EINVAL); 508dacca5f0SHans Verkuil 509ec917d77SDaniel Oakley for (i = 0; i < vdebayer->sink_fmt.height; i++) 510ec917d77SDaniel Oakley for (j = 0; j < vdebayer->sink_fmt.width; j++) { 511ec917d77SDaniel Oakley vimc_debayer_calc_rgb_sink(vdebayer, sink_frame, i, j, rgb); 512ec917d77SDaniel Oakley vdebayer->set_rgb_src(vdebayer, i, j, rgb); 513dacca5f0SHans Verkuil } 514dacca5f0SHans Verkuil 515ec917d77SDaniel Oakley return vdebayer->src_frame; 516dacca5f0SHans Verkuil } 517dacca5f0SHans Verkuil 518ec917d77SDaniel Oakley static int vimc_debayer_s_ctrl(struct v4l2_ctrl *ctrl) 519dacca5f0SHans Verkuil { 520ec917d77SDaniel Oakley struct vimc_debayer_device *vdebayer = 521ec917d77SDaniel Oakley container_of(ctrl->handler, struct vimc_debayer_device, hdl); 522dacca5f0SHans Verkuil 523dacca5f0SHans Verkuil switch (ctrl->id) { 524dacca5f0SHans Verkuil case VIMC_CID_MEAN_WIN_SIZE: 525ec917d77SDaniel Oakley vdebayer->mean_win_size = ctrl->val; 526dacca5f0SHans Verkuil break; 527dacca5f0SHans Verkuil default: 528dacca5f0SHans Verkuil return -EINVAL; 529dacca5f0SHans Verkuil } 530dacca5f0SHans Verkuil return 0; 531dacca5f0SHans Verkuil } 532dacca5f0SHans Verkuil 533ec917d77SDaniel Oakley static const struct v4l2_ctrl_ops vimc_debayer_ctrl_ops = { 534ec917d77SDaniel Oakley .s_ctrl = vimc_debayer_s_ctrl, 535dacca5f0SHans Verkuil }; 536dacca5f0SHans Verkuil 537ec917d77SDaniel Oakley static void vimc_debayer_release(struct vimc_ent_device *ved) 538dacca5f0SHans Verkuil { 539ec917d77SDaniel Oakley struct vimc_debayer_device *vdebayer = 540ec917d77SDaniel Oakley container_of(ved, struct vimc_debayer_device, ved); 541dacca5f0SHans Verkuil 542ec917d77SDaniel Oakley v4l2_ctrl_handler_free(&vdebayer->hdl); 543ec917d77SDaniel Oakley media_entity_cleanup(vdebayer->ved.ent); 544ec917d77SDaniel Oakley kfree(vdebayer); 545dacca5f0SHans Verkuil } 546dacca5f0SHans Verkuil 547ec917d77SDaniel Oakley static const struct v4l2_ctrl_config vimc_debayer_ctrl_class = { 548dacca5f0SHans Verkuil .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY, 549dacca5f0SHans Verkuil .id = VIMC_CID_VIMC_CLASS, 550dacca5f0SHans Verkuil .name = "VIMC Controls", 551dacca5f0SHans Verkuil .type = V4L2_CTRL_TYPE_CTRL_CLASS, 552dacca5f0SHans Verkuil }; 553dacca5f0SHans Verkuil 554ec917d77SDaniel Oakley static const struct v4l2_ctrl_config vimc_debayer_ctrl_mean_win_size = { 555ec917d77SDaniel Oakley .ops = &vimc_debayer_ctrl_ops, 556dacca5f0SHans Verkuil .id = VIMC_CID_MEAN_WIN_SIZE, 557dacca5f0SHans Verkuil .name = "Debayer Mean Window Size", 558dacca5f0SHans Verkuil .type = V4L2_CTRL_TYPE_INTEGER, 559dacca5f0SHans Verkuil .min = 1, 560dacca5f0SHans Verkuil .max = 25, 561dacca5f0SHans Verkuil .step = 2, 562dacca5f0SHans Verkuil .def = 3, 563dacca5f0SHans Verkuil }; 564dacca5f0SHans Verkuil 565ec917d77SDaniel Oakley static struct vimc_ent_device *vimc_debayer_add(struct vimc_device *vimc, 566dacca5f0SHans Verkuil const char *vcfg_name) 567dacca5f0SHans Verkuil { 568dacca5f0SHans Verkuil struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; 569ec917d77SDaniel Oakley struct vimc_debayer_device *vdebayer; 570dacca5f0SHans Verkuil int ret; 571dacca5f0SHans Verkuil 572ec917d77SDaniel Oakley /* Allocate the vdebayer struct */ 573ec917d77SDaniel Oakley vdebayer = kzalloc(sizeof(*vdebayer), GFP_KERNEL); 574ec917d77SDaniel Oakley if (!vdebayer) 575dacca5f0SHans Verkuil return ERR_PTR(-ENOMEM); 576dacca5f0SHans Verkuil 577dacca5f0SHans Verkuil /* Create controls: */ 578ec917d77SDaniel Oakley v4l2_ctrl_handler_init(&vdebayer->hdl, 2); 579ec917d77SDaniel Oakley v4l2_ctrl_new_custom(&vdebayer->hdl, &vimc_debayer_ctrl_class, NULL); 580ec917d77SDaniel Oakley v4l2_ctrl_new_custom(&vdebayer->hdl, &vimc_debayer_ctrl_mean_win_size, NULL); 581ec917d77SDaniel Oakley vdebayer->sd.ctrl_handler = &vdebayer->hdl; 582ec917d77SDaniel Oakley if (vdebayer->hdl.error) { 583ec917d77SDaniel Oakley ret = vdebayer->hdl.error; 584ec917d77SDaniel Oakley goto err_free_vdebayer; 585dacca5f0SHans Verkuil } 586dacca5f0SHans Verkuil 587dacca5f0SHans Verkuil /* Initialize ved and sd */ 588ec917d77SDaniel Oakley vdebayer->pads[0].flags = MEDIA_PAD_FL_SINK; 589ec917d77SDaniel Oakley vdebayer->pads[1].flags = MEDIA_PAD_FL_SOURCE; 590dacca5f0SHans Verkuil 591ec917d77SDaniel Oakley ret = vimc_ent_sd_register(&vdebayer->ved, &vdebayer->sd, v4l2_dev, 592dacca5f0SHans Verkuil vcfg_name, 593dacca5f0SHans Verkuil MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, 594ec917d77SDaniel Oakley vdebayer->pads, &vimc_debayer_ops); 595dacca5f0SHans Verkuil if (ret) 596dacca5f0SHans Verkuil goto err_free_hdl; 597dacca5f0SHans Verkuil 598ec917d77SDaniel Oakley vdebayer->ved.process_frame = vimc_debayer_process_frame; 599ec917d77SDaniel Oakley vdebayer->ved.dev = vimc->mdev.dev; 600ec917d77SDaniel Oakley vdebayer->mean_win_size = vimc_debayer_ctrl_mean_win_size.def; 601dacca5f0SHans Verkuil 602dacca5f0SHans Verkuil /* Initialize the frame format */ 603ec917d77SDaniel Oakley vdebayer->sink_fmt = sink_fmt_default; 604dacca5f0SHans Verkuil /* 605dacca5f0SHans Verkuil * TODO: Add support for more output formats, we only support 606dacca5f0SHans Verkuil * RGB888 for now 607dacca5f0SHans Verkuil * NOTE: the src format is always the same as the sink, except 608dacca5f0SHans Verkuil * for the code 609dacca5f0SHans Verkuil */ 610ec917d77SDaniel Oakley vdebayer->src_code = MEDIA_BUS_FMT_RGB888_1X24; 611ec917d77SDaniel Oakley vdebayer->set_rgb_src = vimc_debayer_process_rgb_frame; 612dacca5f0SHans Verkuil 613ec917d77SDaniel Oakley return &vdebayer->ved; 614dacca5f0SHans Verkuil 615dacca5f0SHans Verkuil err_free_hdl: 616ec917d77SDaniel Oakley v4l2_ctrl_handler_free(&vdebayer->hdl); 617ec917d77SDaniel Oakley err_free_vdebayer: 618ec917d77SDaniel Oakley kfree(vdebayer); 619dacca5f0SHans Verkuil 620dacca5f0SHans Verkuil return ERR_PTR(ret); 621dacca5f0SHans Verkuil } 622dacca5f0SHans Verkuil 623ec917d77SDaniel Oakley struct vimc_ent_type vimc_debayer_type = { 624ec917d77SDaniel Oakley .add = vimc_debayer_add, 625ec917d77SDaniel Oakley .release = vimc_debayer_release 626dacca5f0SHans Verkuil }; 627