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