1/* 2 * vsp1_lif.c -- R-Car VSP1 LCD Controller Interface 3 * 4 * Copyright (C) 2013-2014 Renesas Electronics 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_lif.h" 21 22#define LIF_MIN_SIZE 2U 23#define LIF_MAX_SIZE 2048U 24 25/* ----------------------------------------------------------------------------- 26 * Device Access 27 */ 28 29static inline u32 vsp1_lif_read(struct vsp1_lif *lif, u32 reg) 30{ 31 return vsp1_read(lif->entity.vsp1, reg); 32} 33 34static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data) 35{ 36 vsp1_write(lif->entity.vsp1, reg, data); 37} 38 39/* ----------------------------------------------------------------------------- 40 * V4L2 Subdevice Core Operations 41 */ 42 43static int lif_s_stream(struct v4l2_subdev *subdev, int enable) 44{ 45 const struct v4l2_mbus_framefmt *format; 46 struct vsp1_lif *lif = to_lif(subdev); 47 unsigned int hbth = 1300; 48 unsigned int obth = 400; 49 unsigned int lbth = 200; 50 51 if (!enable) { 52 vsp1_lif_write(lif, VI6_LIF_CTRL, 0); 53 return 0; 54 } 55 56 format = &lif->entity.formats[LIF_PAD_SOURCE]; 57 58 obth = min(obth, (format->width + 1) / 2 * format->height - 4); 59 60 vsp1_lif_write(lif, VI6_LIF_CSBTH, 61 (hbth << VI6_LIF_CSBTH_HBTH_SHIFT) | 62 (lbth << VI6_LIF_CSBTH_LBTH_SHIFT)); 63 64 vsp1_lif_write(lif, VI6_LIF_CTRL, 65 (obth << VI6_LIF_CTRL_OBTH_SHIFT) | 66 (format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) | 67 VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN); 68 69 return 0; 70} 71 72/* ----------------------------------------------------------------------------- 73 * V4L2 Subdevice Pad Operations 74 */ 75 76static int lif_enum_mbus_code(struct v4l2_subdev *subdev, 77 struct v4l2_subdev_pad_config *cfg, 78 struct v4l2_subdev_mbus_code_enum *code) 79{ 80 static const unsigned int codes[] = { 81 MEDIA_BUS_FMT_ARGB8888_1X32, 82 MEDIA_BUS_FMT_AYUV8_1X32, 83 }; 84 struct vsp1_lif *lif = to_lif(subdev); 85 86 if (code->pad == LIF_PAD_SINK) { 87 if (code->index >= ARRAY_SIZE(codes)) 88 return -EINVAL; 89 90 code->code = codes[code->index]; 91 } else { 92 struct v4l2_mbus_framefmt *format; 93 94 /* The LIF can't perform format conversion, the sink format is 95 * always identical to the source format. 96 */ 97 if (code->index) 98 return -EINVAL; 99 100 format = vsp1_entity_get_pad_format(&lif->entity, cfg, 101 LIF_PAD_SINK, code->which); 102 code->code = format->code; 103 } 104 105 return 0; 106} 107 108static int lif_enum_frame_size(struct v4l2_subdev *subdev, 109 struct v4l2_subdev_pad_config *cfg, 110 struct v4l2_subdev_frame_size_enum *fse) 111{ 112 struct vsp1_lif *lif = to_lif(subdev); 113 struct v4l2_mbus_framefmt *format; 114 115 format = vsp1_entity_get_pad_format(&lif->entity, cfg, LIF_PAD_SINK, 116 fse->which); 117 118 if (fse->index || fse->code != format->code) 119 return -EINVAL; 120 121 if (fse->pad == LIF_PAD_SINK) { 122 fse->min_width = LIF_MIN_SIZE; 123 fse->max_width = LIF_MAX_SIZE; 124 fse->min_height = LIF_MIN_SIZE; 125 fse->max_height = LIF_MAX_SIZE; 126 } else { 127 fse->min_width = format->width; 128 fse->max_width = format->width; 129 fse->min_height = format->height; 130 fse->max_height = format->height; 131 } 132 133 return 0; 134} 135 136static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, 137 struct v4l2_subdev_format *fmt) 138{ 139 struct vsp1_lif *lif = to_lif(subdev); 140 141 fmt->format = *vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad, 142 fmt->which); 143 144 return 0; 145} 146 147static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, 148 struct v4l2_subdev_format *fmt) 149{ 150 struct vsp1_lif *lif = to_lif(subdev); 151 struct v4l2_mbus_framefmt *format; 152 153 /* Default to YUV if the requested format is not supported. */ 154 if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && 155 fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) 156 fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; 157 158 format = vsp1_entity_get_pad_format(&lif->entity, cfg, fmt->pad, 159 fmt->which); 160 161 if (fmt->pad == LIF_PAD_SOURCE) { 162 /* The LIF source format is always identical to its sink 163 * format. 164 */ 165 fmt->format = *format; 166 return 0; 167 } 168 169 format->code = fmt->format.code; 170 format->width = clamp_t(unsigned int, fmt->format.width, 171 LIF_MIN_SIZE, LIF_MAX_SIZE); 172 format->height = clamp_t(unsigned int, fmt->format.height, 173 LIF_MIN_SIZE, LIF_MAX_SIZE); 174 format->field = V4L2_FIELD_NONE; 175 format->colorspace = V4L2_COLORSPACE_SRGB; 176 177 fmt->format = *format; 178 179 /* Propagate the format to the source pad. */ 180 format = vsp1_entity_get_pad_format(&lif->entity, cfg, LIF_PAD_SOURCE, 181 fmt->which); 182 *format = fmt->format; 183 184 return 0; 185} 186 187/* ----------------------------------------------------------------------------- 188 * V4L2 Subdevice Operations 189 */ 190 191static struct v4l2_subdev_video_ops lif_video_ops = { 192 .s_stream = lif_s_stream, 193}; 194 195static struct v4l2_subdev_pad_ops lif_pad_ops = { 196 .enum_mbus_code = lif_enum_mbus_code, 197 .enum_frame_size = lif_enum_frame_size, 198 .get_fmt = lif_get_format, 199 .set_fmt = lif_set_format, 200}; 201 202static struct v4l2_subdev_ops lif_ops = { 203 .video = &lif_video_ops, 204 .pad = &lif_pad_ops, 205}; 206 207/* ----------------------------------------------------------------------------- 208 * Initialization and Cleanup 209 */ 210 211struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1) 212{ 213 struct v4l2_subdev *subdev; 214 struct vsp1_lif *lif; 215 int ret; 216 217 lif = devm_kzalloc(vsp1->dev, sizeof(*lif), GFP_KERNEL); 218 if (lif == NULL) 219 return ERR_PTR(-ENOMEM); 220 221 lif->entity.type = VSP1_ENTITY_LIF; 222 223 ret = vsp1_entity_init(vsp1, &lif->entity, 2); 224 if (ret < 0) 225 return ERR_PTR(ret); 226 227 /* Initialize the V4L2 subdev. */ 228 subdev = &lif->entity.subdev; 229 v4l2_subdev_init(subdev, &lif_ops); 230 231 subdev->entity.ops = &vsp1_media_ops; 232 subdev->internal_ops = &vsp1_subdev_internal_ops; 233 snprintf(subdev->name, sizeof(subdev->name), "%s lif", 234 dev_name(vsp1->dev)); 235 v4l2_set_subdevdata(subdev, lif); 236 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 237 238 vsp1_entity_init_formats(subdev, NULL); 239 240 return lif; 241} 242