imx-media-internal-sd.c (6681B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Media driver for Freescale i.MX5/6 SOC 4 * 5 * Adds the IPU internal subdevices and the media links between them. 6 * 7 * Copyright (c) 2016 Mentor Graphics Inc. 8 */ 9#include <linux/platform_device.h> 10#include "imx-media.h" 11 12/* max pads per internal-sd */ 13#define MAX_INTERNAL_PADS 8 14/* max links per internal-sd pad */ 15#define MAX_INTERNAL_LINKS 8 16 17struct internal_subdev; 18 19struct internal_link { 20 int remote; 21 int local_pad; 22 int remote_pad; 23}; 24 25struct internal_pad { 26 int num_links; 27 struct internal_link link[MAX_INTERNAL_LINKS]; 28}; 29 30struct internal_subdev { 31 u32 grp_id; 32 struct internal_pad pad[MAX_INTERNAL_PADS]; 33 34 struct v4l2_subdev * (*sync_register)(struct v4l2_device *v4l2_dev, 35 struct device *ipu_dev, 36 struct ipu_soc *ipu, 37 u32 grp_id); 38 int (*sync_unregister)(struct v4l2_subdev *sd); 39}; 40 41static const struct internal_subdev int_subdev[NUM_IPU_SUBDEVS] = { 42 [IPU_CSI0] = { 43 .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI0, 44 .pad[CSI_SRC_PAD_DIRECT] = { 45 .num_links = 2, 46 .link = { 47 { 48 .local_pad = CSI_SRC_PAD_DIRECT, 49 .remote = IPU_IC_PRP, 50 .remote_pad = PRP_SINK_PAD, 51 }, { 52 .local_pad = CSI_SRC_PAD_DIRECT, 53 .remote = IPU_VDIC, 54 .remote_pad = VDIC_SINK_PAD_DIRECT, 55 }, 56 }, 57 }, 58 }, 59 60 [IPU_CSI1] = { 61 .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI1, 62 .pad[CSI_SRC_PAD_DIRECT] = { 63 .num_links = 2, 64 .link = { 65 { 66 .local_pad = CSI_SRC_PAD_DIRECT, 67 .remote = IPU_IC_PRP, 68 .remote_pad = PRP_SINK_PAD, 69 }, { 70 .local_pad = CSI_SRC_PAD_DIRECT, 71 .remote = IPU_VDIC, 72 .remote_pad = VDIC_SINK_PAD_DIRECT, 73 }, 74 }, 75 }, 76 }, 77 78 [IPU_VDIC] = { 79 .grp_id = IMX_MEDIA_GRP_ID_IPU_VDIC, 80 .sync_register = imx_media_vdic_register, 81 .sync_unregister = imx_media_vdic_unregister, 82 .pad[VDIC_SRC_PAD_DIRECT] = { 83 .num_links = 1, 84 .link = { 85 { 86 .local_pad = VDIC_SRC_PAD_DIRECT, 87 .remote = IPU_IC_PRP, 88 .remote_pad = PRP_SINK_PAD, 89 }, 90 }, 91 }, 92 }, 93 94 [IPU_IC_PRP] = { 95 .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRP, 96 .sync_register = imx_media_ic_register, 97 .sync_unregister = imx_media_ic_unregister, 98 .pad[PRP_SRC_PAD_PRPENC] = { 99 .num_links = 1, 100 .link = { 101 { 102 .local_pad = PRP_SRC_PAD_PRPENC, 103 .remote = IPU_IC_PRPENC, 104 .remote_pad = PRPENCVF_SINK_PAD, 105 }, 106 }, 107 }, 108 .pad[PRP_SRC_PAD_PRPVF] = { 109 .num_links = 1, 110 .link = { 111 { 112 .local_pad = PRP_SRC_PAD_PRPVF, 113 .remote = IPU_IC_PRPVF, 114 .remote_pad = PRPENCVF_SINK_PAD, 115 }, 116 }, 117 }, 118 }, 119 120 [IPU_IC_PRPENC] = { 121 .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPENC, 122 .sync_register = imx_media_ic_register, 123 .sync_unregister = imx_media_ic_unregister, 124 }, 125 126 [IPU_IC_PRPVF] = { 127 .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPVF, 128 .sync_register = imx_media_ic_register, 129 .sync_unregister = imx_media_ic_unregister, 130 }, 131}; 132 133static int create_internal_link(struct imx_media_dev *imxmd, 134 struct v4l2_subdev *src, 135 struct v4l2_subdev *sink, 136 const struct internal_link *link) 137{ 138 int ret; 139 140 /* skip if this link already created */ 141 if (media_entity_find_link(&src->entity.pads[link->local_pad], 142 &sink->entity.pads[link->remote_pad])) 143 return 0; 144 145 dev_dbg(imxmd->md.dev, "%s:%d -> %s:%d\n", 146 src->name, link->local_pad, 147 sink->name, link->remote_pad); 148 149 ret = media_create_pad_link(&src->entity, link->local_pad, 150 &sink->entity, link->remote_pad, 0); 151 if (ret) 152 v4l2_err(&imxmd->v4l2_dev, "%s failed: %d\n", __func__, ret); 153 154 return ret; 155} 156 157static int create_ipu_internal_links(struct imx_media_dev *imxmd, 158 const struct internal_subdev *intsd, 159 struct v4l2_subdev *sd, 160 int ipu_id) 161{ 162 const struct internal_pad *intpad; 163 const struct internal_link *link; 164 struct media_pad *pad; 165 int i, j, ret; 166 167 /* create the source->sink links */ 168 for (i = 0; i < sd->entity.num_pads; i++) { 169 intpad = &intsd->pad[i]; 170 pad = &sd->entity.pads[i]; 171 172 if (!(pad->flags & MEDIA_PAD_FL_SOURCE)) 173 continue; 174 175 for (j = 0; j < intpad->num_links; j++) { 176 struct v4l2_subdev *sink; 177 178 link = &intpad->link[j]; 179 sink = imxmd->sync_sd[ipu_id][link->remote]; 180 181 ret = create_internal_link(imxmd, sd, sink, link); 182 if (ret) 183 return ret; 184 } 185 } 186 187 return 0; 188} 189 190int imx_media_register_ipu_internal_subdevs(struct imx_media_dev *imxmd, 191 struct v4l2_subdev *csi) 192{ 193 struct device *ipu_dev = csi->dev->parent; 194 const struct internal_subdev *intsd; 195 struct v4l2_subdev *sd; 196 struct ipu_soc *ipu; 197 int i, ipu_id, ret; 198 199 ipu = dev_get_drvdata(ipu_dev); 200 if (!ipu) { 201 v4l2_err(&imxmd->v4l2_dev, "invalid IPU device!\n"); 202 return -ENODEV; 203 } 204 205 ipu_id = ipu_get_num(ipu); 206 if (ipu_id > 1) { 207 v4l2_err(&imxmd->v4l2_dev, "invalid IPU id %d!\n", ipu_id); 208 return -ENODEV; 209 } 210 211 mutex_lock(&imxmd->mutex); 212 213 /* record this IPU */ 214 if (!imxmd->ipu[ipu_id]) 215 imxmd->ipu[ipu_id] = ipu; 216 217 /* register the synchronous subdevs */ 218 for (i = 0; i < NUM_IPU_SUBDEVS; i++) { 219 intsd = &int_subdev[i]; 220 221 sd = imxmd->sync_sd[ipu_id][i]; 222 223 /* 224 * skip if this sync subdev already registered or its 225 * not a sync subdev (one of the CSIs) 226 */ 227 if (sd || !intsd->sync_register) 228 continue; 229 230 mutex_unlock(&imxmd->mutex); 231 sd = intsd->sync_register(&imxmd->v4l2_dev, ipu_dev, ipu, 232 intsd->grp_id); 233 mutex_lock(&imxmd->mutex); 234 if (IS_ERR(sd)) { 235 ret = PTR_ERR(sd); 236 goto err_unwind; 237 } 238 239 imxmd->sync_sd[ipu_id][i] = sd; 240 } 241 242 /* 243 * all the sync subdevs are registered, create the media links 244 * between them. 245 */ 246 for (i = 0; i < NUM_IPU_SUBDEVS; i++) { 247 intsd = &int_subdev[i]; 248 249 if (intsd->grp_id == csi->grp_id) { 250 sd = csi; 251 } else { 252 sd = imxmd->sync_sd[ipu_id][i]; 253 if (!sd) 254 continue; 255 } 256 257 ret = create_ipu_internal_links(imxmd, intsd, sd, ipu_id); 258 if (ret) { 259 mutex_unlock(&imxmd->mutex); 260 imx_media_unregister_ipu_internal_subdevs(imxmd); 261 return ret; 262 } 263 } 264 265 mutex_unlock(&imxmd->mutex); 266 return 0; 267 268err_unwind: 269 while (--i >= 0) { 270 intsd = &int_subdev[i]; 271 sd = imxmd->sync_sd[ipu_id][i]; 272 if (!sd || !intsd->sync_unregister) 273 continue; 274 mutex_unlock(&imxmd->mutex); 275 intsd->sync_unregister(sd); 276 mutex_lock(&imxmd->mutex); 277 } 278 279 mutex_unlock(&imxmd->mutex); 280 return ret; 281} 282 283void imx_media_unregister_ipu_internal_subdevs(struct imx_media_dev *imxmd) 284{ 285 const struct internal_subdev *intsd; 286 struct v4l2_subdev *sd; 287 int i, j; 288 289 mutex_lock(&imxmd->mutex); 290 291 for (i = 0; i < 2; i++) { 292 for (j = 0; j < NUM_IPU_SUBDEVS; j++) { 293 intsd = &int_subdev[j]; 294 sd = imxmd->sync_sd[i][j]; 295 296 if (!sd || !intsd->sync_unregister) 297 continue; 298 299 mutex_unlock(&imxmd->mutex); 300 intsd->sync_unregister(sd); 301 mutex_lock(&imxmd->mutex); 302 } 303 } 304 305 mutex_unlock(&imxmd->mutex); 306}