1/* 2 * vsp1_hsit.c -- R-Car VSP1 Hue Saturation value (Inverse) Transform 3 * 4 * Copyright (C) 2013 Renesas Corporation 5 * 6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 */ 13 14#include <linux/device.h> 15#include <linux/gfp.h> 16 17#include <media/v4l2-subdev.h> 18 19#include "vsp1.h" 20#include "vsp1_hsit.h" 21 22#define HSIT_MIN_SIZE 4U 23#define HSIT_MAX_SIZE 8190U 24 25/* ----------------------------------------------------------------------------- 26 * Device Access 27 */ 28 29static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data) 30{ 31 vsp1_write(hsit->entity.vsp1, reg, data); 32} 33 34/* ----------------------------------------------------------------------------- 35 * V4L2 Subdevice Core Operations 36 */ 37 38static int hsit_s_stream(struct v4l2_subdev *subdev, int enable) 39{ 40 struct vsp1_hsit *hsit = to_hsit(subdev); 41 42 if (!enable) 43 return 0; 44 45 if (hsit->inverse) 46 vsp1_hsit_write(hsit, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); 47 else 48 vsp1_hsit_write(hsit, VI6_HST_CTRL, VI6_HST_CTRL_EN); 49 50 return 0; 51} 52 53/* ----------------------------------------------------------------------------- 54 * V4L2 Subdevice Pad Operations 55 */ 56 57static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, 58 struct v4l2_subdev_pad_config *cfg, 59 struct v4l2_subdev_mbus_code_enum *code) 60{ 61 struct vsp1_hsit *hsit = to_hsit(subdev); 62 63 if (code->index > 0) 64 return -EINVAL; 65 66 if ((code->pad == HSIT_PAD_SINK && !hsit->inverse) | 67 (code->pad == HSIT_PAD_SOURCE && hsit->inverse)) 68 code->code = MEDIA_BUS_FMT_ARGB8888_1X32; 69 else 70 code->code = MEDIA_BUS_FMT_AHSV8888_1X32; 71 72 return 0; 73} 74 75static int hsit_enum_frame_size(struct v4l2_subdev *subdev, 76 struct v4l2_subdev_pad_config *cfg, 77 struct v4l2_subdev_frame_size_enum *fse) 78{ 79 struct vsp1_hsit *hsit = to_hsit(subdev); 80 struct v4l2_mbus_framefmt *format; 81 82 format = vsp1_entity_get_pad_format(&hsit->entity, cfg, fse->pad, 83 fse->which); 84 85 if (fse->index || fse->code != format->code) 86 return -EINVAL; 87 88 if (fse->pad == HSIT_PAD_SINK) { 89 fse->min_width = HSIT_MIN_SIZE; 90 fse->max_width = HSIT_MAX_SIZE; 91 fse->min_height = HSIT_MIN_SIZE; 92 fse->max_height = HSIT_MAX_SIZE; 93 } else { 94 /* The size on the source pad are fixed and always identical to 95 * the size on the sink pad. 96 */ 97 fse->min_width = format->width; 98 fse->max_width = format->width; 99 fse->min_height = format->height; 100 fse->max_height = format->height; 101 } 102 103 return 0; 104} 105 106static int hsit_get_format(struct v4l2_subdev *subdev, 107 struct v4l2_subdev_pad_config *cfg, 108 struct v4l2_subdev_format *fmt) 109{ 110 struct vsp1_hsit *hsit = to_hsit(subdev); 111 112 fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad, 113 fmt->which); 114 115 return 0; 116} 117 118static int hsit_set_format(struct v4l2_subdev *subdev, 119 struct v4l2_subdev_pad_config *cfg, 120 struct v4l2_subdev_format *fmt) 121{ 122 struct vsp1_hsit *hsit = to_hsit(subdev); 123 struct v4l2_mbus_framefmt *format; 124 125 format = vsp1_entity_get_pad_format(&hsit->entity, cfg, fmt->pad, 126 fmt->which); 127 128 if (fmt->pad == HSIT_PAD_SOURCE) { 129 /* The HST and HSI output format code and resolution can't be 130 * modified. 131 */ 132 fmt->format = *format; 133 return 0; 134 } 135 136 format->code = hsit->inverse ? MEDIA_BUS_FMT_AHSV8888_1X32 137 : MEDIA_BUS_FMT_ARGB8888_1X32; 138 format->width = clamp_t(unsigned int, fmt->format.width, 139 HSIT_MIN_SIZE, HSIT_MAX_SIZE); 140 format->height = clamp_t(unsigned int, fmt->format.height, 141 HSIT_MIN_SIZE, HSIT_MAX_SIZE); 142 format->field = V4L2_FIELD_NONE; 143 format->colorspace = V4L2_COLORSPACE_SRGB; 144 145 fmt->format = *format; 146 147 /* Propagate the format to the source pad. */ 148 format = vsp1_entity_get_pad_format(&hsit->entity, cfg, HSIT_PAD_SOURCE, 149 fmt->which); 150 *format = fmt->format; 151 format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 152 : MEDIA_BUS_FMT_AHSV8888_1X32; 153 154 return 0; 155} 156 157/* ----------------------------------------------------------------------------- 158 * V4L2 Subdevice Operations 159 */ 160 161static struct v4l2_subdev_video_ops hsit_video_ops = { 162 .s_stream = hsit_s_stream, 163}; 164 165static struct v4l2_subdev_pad_ops hsit_pad_ops = { 166 .enum_mbus_code = hsit_enum_mbus_code, 167 .enum_frame_size = hsit_enum_frame_size, 168 .get_fmt = hsit_get_format, 169 .set_fmt = hsit_set_format, 170}; 171 172static struct v4l2_subdev_ops hsit_ops = { 173 .video = &hsit_video_ops, 174 .pad = &hsit_pad_ops, 175}; 176 177/* ----------------------------------------------------------------------------- 178 * Initialization and Cleanup 179 */ 180 181struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) 182{ 183 struct v4l2_subdev *subdev; 184 struct vsp1_hsit *hsit; 185 int ret; 186 187 hsit = devm_kzalloc(vsp1->dev, sizeof(*hsit), GFP_KERNEL); 188 if (hsit == NULL) 189 return ERR_PTR(-ENOMEM); 190 191 hsit->inverse = inverse; 192 193 if (inverse) 194 hsit->entity.type = VSP1_ENTITY_HSI; 195 else 196 hsit->entity.type = VSP1_ENTITY_HST; 197 198 ret = vsp1_entity_init(vsp1, &hsit->entity, 2); 199 if (ret < 0) 200 return ERR_PTR(ret); 201 202 /* Initialize the V4L2 subdev. */ 203 subdev = &hsit->entity.subdev; 204 v4l2_subdev_init(subdev, &hsit_ops); 205 206 subdev->entity.ops = &vsp1_media_ops; 207 subdev->internal_ops = &vsp1_subdev_internal_ops; 208 snprintf(subdev->name, sizeof(subdev->name), "%s %s", 209 dev_name(vsp1->dev), inverse ? "hsi" : "hst"); 210 v4l2_set_subdevdata(subdev, hsit); 211 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 212 213 vsp1_entity_init_formats(subdev, NULL); 214 215 return hsit; 216} 217