1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * vsp1_hsit.c -- R-Car VSP1 Hue Saturation value (Inverse) Transform 4 * 5 * Copyright (C) 2013 Renesas Corporation 6 * 7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 8 */ 9 10 #include <linux/device.h> 11 #include <linux/gfp.h> 12 13 #include <media/v4l2-subdev.h> 14 15 #include "vsp1.h" 16 #include "vsp1_dl.h" 17 #include "vsp1_entity.h" 18 #include "vsp1_hsit.h" 19 20 #define HSIT_MIN_SIZE 4U 21 #define HSIT_MAX_SIZE 8190U 22 23 /* ----------------------------------------------------------------------------- 24 * Device Access 25 */ 26 27 static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, 28 struct vsp1_dl_body *dlb, u32 reg, u32 data) 29 { 30 vsp1_dl_body_write(dlb, reg, data); 31 } 32 33 /* ----------------------------------------------------------------------------- 34 * V4L2 Subdevice Operations 35 */ 36 37 static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, 38 struct v4l2_subdev_state *sd_state, 39 struct v4l2_subdev_mbus_code_enum *code) 40 { 41 struct vsp1_hsit *hsit = to_hsit(subdev); 42 43 if (code->index > 0) 44 return -EINVAL; 45 46 if ((code->pad == HSIT_PAD_SINK && !hsit->inverse) | 47 (code->pad == HSIT_PAD_SOURCE && hsit->inverse)) 48 code->code = MEDIA_BUS_FMT_ARGB8888_1X32; 49 else 50 code->code = MEDIA_BUS_FMT_AHSV8888_1X32; 51 52 return 0; 53 } 54 55 static int hsit_enum_frame_size(struct v4l2_subdev *subdev, 56 struct v4l2_subdev_state *sd_state, 57 struct v4l2_subdev_frame_size_enum *fse) 58 { 59 return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, 60 HSIT_MIN_SIZE, 61 HSIT_MIN_SIZE, HSIT_MAX_SIZE, 62 HSIT_MAX_SIZE); 63 } 64 65 static int hsit_set_format(struct v4l2_subdev *subdev, 66 struct v4l2_subdev_state *sd_state, 67 struct v4l2_subdev_format *fmt) 68 { 69 struct vsp1_hsit *hsit = to_hsit(subdev); 70 struct v4l2_subdev_state *state; 71 struct v4l2_mbus_framefmt *format; 72 int ret = 0; 73 74 mutex_lock(&hsit->entity.lock); 75 76 state = vsp1_entity_get_state(&hsit->entity, sd_state, fmt->which); 77 if (!state) { 78 ret = -EINVAL; 79 goto done; 80 } 81 82 format = v4l2_subdev_state_get_format(state, fmt->pad); 83 84 if (fmt->pad == HSIT_PAD_SOURCE) { 85 /* 86 * The HST and HSI output format code and resolution can't be 87 * modified. 88 */ 89 fmt->format = *format; 90 goto done; 91 } 92 93 format->code = hsit->inverse ? MEDIA_BUS_FMT_AHSV8888_1X32 94 : MEDIA_BUS_FMT_ARGB8888_1X32; 95 format->width = clamp_t(unsigned int, fmt->format.width, 96 HSIT_MIN_SIZE, HSIT_MAX_SIZE); 97 format->height = clamp_t(unsigned int, fmt->format.height, 98 HSIT_MIN_SIZE, HSIT_MAX_SIZE); 99 format->field = V4L2_FIELD_NONE; 100 101 format->colorspace = fmt->format.colorspace; 102 format->xfer_func = fmt->format.xfer_func; 103 format->ycbcr_enc = fmt->format.ycbcr_enc; 104 format->quantization = fmt->format.quantization; 105 106 vsp1_entity_adjust_color_space(format); 107 108 fmt->format = *format; 109 110 /* Propagate the format to the source pad. */ 111 format = v4l2_subdev_state_get_format(state, HSIT_PAD_SOURCE); 112 *format = fmt->format; 113 format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 114 : MEDIA_BUS_FMT_AHSV8888_1X32; 115 116 vsp1_entity_adjust_color_space(format); 117 118 done: 119 mutex_unlock(&hsit->entity.lock); 120 return ret; 121 } 122 123 static const struct v4l2_subdev_pad_ops hsit_pad_ops = { 124 .enum_mbus_code = hsit_enum_mbus_code, 125 .enum_frame_size = hsit_enum_frame_size, 126 .get_fmt = vsp1_subdev_get_pad_format, 127 .set_fmt = hsit_set_format, 128 }; 129 130 static const struct v4l2_subdev_ops hsit_ops = { 131 .pad = &hsit_pad_ops, 132 }; 133 134 /* ----------------------------------------------------------------------------- 135 * VSP1 Entity Operations 136 */ 137 138 static void hsit_configure_stream(struct vsp1_entity *entity, 139 struct v4l2_subdev_state *state, 140 struct vsp1_pipeline *pipe, 141 struct vsp1_dl_list *dl, 142 struct vsp1_dl_body *dlb) 143 { 144 struct vsp1_hsit *hsit = to_hsit(&entity->subdev); 145 146 if (hsit->inverse) 147 vsp1_hsit_write(hsit, dlb, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); 148 else 149 vsp1_hsit_write(hsit, dlb, VI6_HST_CTRL, VI6_HST_CTRL_EN); 150 } 151 152 static const struct vsp1_entity_operations hsit_entity_ops = { 153 .configure_stream = hsit_configure_stream, 154 }; 155 156 /* ----------------------------------------------------------------------------- 157 * Initialization and Cleanup 158 */ 159 160 struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) 161 { 162 struct vsp1_hsit *hsit; 163 int ret; 164 165 hsit = devm_kzalloc(vsp1->dev, sizeof(*hsit), GFP_KERNEL); 166 if (hsit == NULL) 167 return ERR_PTR(-ENOMEM); 168 169 hsit->inverse = inverse; 170 171 hsit->entity.ops = &hsit_entity_ops; 172 173 if (inverse) 174 hsit->entity.type = VSP1_ENTITY_HSI; 175 else 176 hsit->entity.type = VSP1_ENTITY_HST; 177 178 ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst", 179 2, &hsit_ops, 180 MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV); 181 if (ret < 0) 182 return ERR_PTR(ret); 183 184 return hsit; 185 } 186