1/* 2 * vsp1_entity.c -- R-Car VSP1 Base Entity 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/media-entity.h> 18#include <media/v4l2-ctrls.h> 19#include <media/v4l2-subdev.h> 20 21#include "vsp1.h" 22#include "vsp1_entity.h" 23#include "vsp1_video.h" 24 25bool vsp1_entity_is_streaming(struct vsp1_entity *entity) 26{ 27 bool streaming; 28 29 mutex_lock(&entity->lock); 30 streaming = entity->streaming; 31 mutex_unlock(&entity->lock); 32 33 return streaming; 34} 35 36int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) 37{ 38 int ret; 39 40 mutex_lock(&entity->lock); 41 entity->streaming = streaming; 42 mutex_unlock(&entity->lock); 43 44 if (!streaming) 45 return 0; 46 47 if (!entity->subdev.ctrl_handler) 48 return 0; 49 50 ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler); 51 if (ret < 0) { 52 mutex_lock(&entity->lock); 53 entity->streaming = false; 54 mutex_unlock(&entity->lock); 55 } 56 57 return ret; 58} 59 60/* ----------------------------------------------------------------------------- 61 * V4L2 Subdevice Operations 62 */ 63 64struct v4l2_mbus_framefmt * 65vsp1_entity_get_pad_format(struct vsp1_entity *entity, 66 struct v4l2_subdev_fh *fh, 67 unsigned int pad, u32 which) 68{ 69 switch (which) { 70 case V4L2_SUBDEV_FORMAT_TRY: 71 return v4l2_subdev_get_try_format(fh, pad); 72 case V4L2_SUBDEV_FORMAT_ACTIVE: 73 return &entity->formats[pad]; 74 default: 75 return NULL; 76 } 77} 78 79/* 80 * vsp1_entity_init_formats - Initialize formats on all pads 81 * @subdev: V4L2 subdevice 82 * @fh: V4L2 subdev file handle 83 * 84 * Initialize all pad formats with default values. If fh is not NULL, try 85 * formats are initialized on the file handle. Otherwise active formats are 86 * initialized on the device. 87 */ 88void vsp1_entity_init_formats(struct v4l2_subdev *subdev, 89 struct v4l2_subdev_fh *fh) 90{ 91 struct v4l2_subdev_format format; 92 unsigned int pad; 93 94 for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { 95 memset(&format, 0, sizeof(format)); 96 97 format.pad = pad; 98 format.which = fh ? V4L2_SUBDEV_FORMAT_TRY 99 : V4L2_SUBDEV_FORMAT_ACTIVE; 100 101 v4l2_subdev_call(subdev, pad, set_fmt, fh, &format); 102 } 103} 104 105static int vsp1_entity_open(struct v4l2_subdev *subdev, 106 struct v4l2_subdev_fh *fh) 107{ 108 vsp1_entity_init_formats(subdev, fh); 109 110 return 0; 111} 112 113const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = { 114 .open = vsp1_entity_open, 115}; 116 117/* ----------------------------------------------------------------------------- 118 * Media Operations 119 */ 120 121static int vsp1_entity_link_setup(struct media_entity *entity, 122 const struct media_pad *local, 123 const struct media_pad *remote, u32 flags) 124{ 125 struct vsp1_entity *source; 126 127 if (!(local->flags & MEDIA_PAD_FL_SOURCE)) 128 return 0; 129 130 source = container_of(local->entity, struct vsp1_entity, subdev.entity); 131 132 if (!source->route) 133 return 0; 134 135 if (flags & MEDIA_LNK_FL_ENABLED) { 136 if (source->sink) 137 return -EBUSY; 138 source->sink = remote->entity; 139 source->sink_pad = remote->index; 140 } else { 141 source->sink = NULL; 142 source->sink_pad = 0; 143 } 144 145 return 0; 146} 147 148const struct media_entity_operations vsp1_media_ops = { 149 .link_setup = vsp1_entity_link_setup, 150 .link_validate = v4l2_subdev_link_validate, 151}; 152 153/* ----------------------------------------------------------------------------- 154 * Initialization 155 */ 156 157static const struct vsp1_route vsp1_routes[] = { 158 { VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE, 159 { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1), 160 VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), } }, 161 { VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } }, 162 { VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } }, 163 { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } }, 164 { VSP1_ENTITY_LUT, 0, VI6_DPR_LUT_ROUTE, { VI6_DPR_NODE_LUT, } }, 165 { VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { VI6_DPR_NODE_RPF(0), } }, 166 { VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { VI6_DPR_NODE_RPF(1), } }, 167 { VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { VI6_DPR_NODE_RPF(2), } }, 168 { VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { VI6_DPR_NODE_RPF(3), } }, 169 { VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { VI6_DPR_NODE_RPF(4), } }, 170 { VSP1_ENTITY_SRU, 0, VI6_DPR_SRU_ROUTE, { VI6_DPR_NODE_SRU, } }, 171 { VSP1_ENTITY_UDS, 0, VI6_DPR_UDS_ROUTE(0), { VI6_DPR_NODE_UDS(0), } }, 172 { VSP1_ENTITY_UDS, 1, VI6_DPR_UDS_ROUTE(1), { VI6_DPR_NODE_UDS(1), } }, 173 { VSP1_ENTITY_UDS, 2, VI6_DPR_UDS_ROUTE(2), { VI6_DPR_NODE_UDS(2), } }, 174 { VSP1_ENTITY_WPF, 0, 0, { VI6_DPR_NODE_WPF(0), } }, 175 { VSP1_ENTITY_WPF, 1, 0, { VI6_DPR_NODE_WPF(1), } }, 176 { VSP1_ENTITY_WPF, 2, 0, { VI6_DPR_NODE_WPF(2), } }, 177 { VSP1_ENTITY_WPF, 3, 0, { VI6_DPR_NODE_WPF(3), } }, 178}; 179 180int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, 181 unsigned int num_pads) 182{ 183 unsigned int i; 184 185 for (i = 0; i < ARRAY_SIZE(vsp1_routes); ++i) { 186 if (vsp1_routes[i].type == entity->type && 187 vsp1_routes[i].index == entity->index) { 188 entity->route = &vsp1_routes[i]; 189 break; 190 } 191 } 192 193 if (i == ARRAY_SIZE(vsp1_routes)) 194 return -EINVAL; 195 196 mutex_init(&entity->lock); 197 198 entity->vsp1 = vsp1; 199 entity->source_pad = num_pads - 1; 200 201 /* Allocate formats and pads. */ 202 entity->formats = devm_kzalloc(vsp1->dev, 203 num_pads * sizeof(*entity->formats), 204 GFP_KERNEL); 205 if (entity->formats == NULL) 206 return -ENOMEM; 207 208 entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads), 209 GFP_KERNEL); 210 if (entity->pads == NULL) 211 return -ENOMEM; 212 213 /* Initialize pads. */ 214 for (i = 0; i < num_pads - 1; ++i) 215 entity->pads[i].flags = MEDIA_PAD_FL_SINK; 216 217 entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE; 218 219 /* Initialize the media entity. */ 220 return media_entity_init(&entity->subdev.entity, num_pads, 221 entity->pads, 0); 222} 223 224void vsp1_entity_destroy(struct vsp1_entity *entity) 225{ 226 if (entity->video) 227 vsp1_video_cleanup(entity->video); 228 if (entity->subdev.ctrl_handler) 229 v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); 230 media_entity_cleanup(&entity->subdev.entity); 231 232 mutex_destroy(&entity->lock); 233} 234