Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] sh_mobile_ceu_camera: add asynchronous subdevice probing support

Use the v4l2-async API to support asynchronous subdevice probing,
including the CSI2 subdevice. Synchronous probing is still supported too.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

authored by

Guennadi Liakhovetski and committed by
Mauro Carvalho Chehab
676d2d4f e09da11d

+190 -101
+97 -37
drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
··· 36 36 #include <linux/pm_runtime.h> 37 37 #include <linux/sched.h> 38 38 39 + #include <media/v4l2-async.h> 39 40 #include <media/v4l2-common.h> 40 41 #include <media/v4l2-dev.h> 41 42 #include <media/soc_camera.h> ··· 97 96 98 97 struct sh_mobile_ceu_dev { 99 98 struct soc_camera_host ici; 99 + /* Asynchronous CSI2 linking */ 100 + struct v4l2_async_subdev *csi2_asd; 101 + struct v4l2_subdev *csi2_sd; 102 + /* Synchronous probing compatibility */ 100 103 struct platform_device *csi2_pdev; 101 104 102 105 unsigned int irq; ··· 189 184 } 190 185 udelay(1); 191 186 } 192 - 193 187 194 188 if (2 != success) { 195 189 dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n"); ··· 538 534 { 539 535 struct v4l2_subdev *sd; 540 536 541 - if (!pcdev->csi2_pdev) 542 - return NULL; 537 + if (pcdev->csi2_sd) 538 + return pcdev->csi2_sd; 543 539 544 - v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev) 545 - if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd)) 546 - return sd; 540 + if (pcdev->csi2_asd) { 541 + char name[] = "sh-mobile-csi2"; 542 + v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev) 543 + if (!strncmp(name, sd->name, sizeof(name) - 1)) { 544 + pcdev->csi2_sd = sd; 545 + return sd; 546 + } 547 + } 547 548 548 549 return NULL; 550 + } 551 + 552 + static struct v4l2_subdev *csi2_subdev(struct sh_mobile_ceu_dev *pcdev, 553 + struct soc_camera_device *icd) 554 + { 555 + struct v4l2_subdev *sd = pcdev->csi2_sd; 556 + 557 + return sd && sd->grp_id == soc_camera_grp_id(icd) ? sd : NULL; 549 558 } 550 559 551 560 static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) ··· 581 564 * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver 582 565 * has not found this soc-camera device among its clients 583 566 */ 584 - if (ret == -ENODEV && csi2_sd) 567 + if (csi2_sd && ret == -ENODEV) 585 568 csi2_sd->grp_id = 0; 586 569 587 570 dev_info(icd->parent, 588 - "SuperH Mobile CEU driver attached to camera %d\n", 589 - icd->devnum); 571 + "SuperH Mobile CEU%s driver attached to camera %d\n", 572 + csi2_sd && csi2_sd->grp_id ? "/CSI-2" : "", icd->devnum); 590 573 591 574 return 0; 592 575 } ··· 602 585 icd->devnum); 603 586 604 587 v4l2_subdev_call(csi2_sd, core, s_power, 0); 605 - if (csi2_sd) 606 - csi2_sd->grp_id = 0; 607 588 } 608 589 609 590 /* Called with .host_lock held */ ··· 723 708 } 724 709 725 710 /* CSI2 special configuration */ 726 - if (pcdev->csi2_pdev) { 711 + if (csi2_subdev(pcdev, icd)) { 727 712 in_width = ((in_width - 2) * 2); 728 713 left_offset *= 2; 729 714 } ··· 780 765 static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev, 781 766 struct soc_camera_device *icd) 782 767 { 783 - if (pcdev->csi2_pdev) { 784 - struct v4l2_subdev *csi2_sd = find_csi2(pcdev); 785 - if (csi2_sd && csi2_sd->grp_id == soc_camera_grp_id(icd)) 786 - return csi2_sd; 787 - } 788 - 789 - return soc_camera_to_subdev(icd); 768 + return csi2_subdev(pcdev, icd) ? : soc_camera_to_subdev(icd); 790 769 } 791 770 792 771 #define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \ ··· 884 875 value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; 885 876 value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; 886 877 887 - if (pcdev->csi2_pdev) /* CSI2 mode */ 878 + if (csi2_subdev(pcdev, icd)) /* CSI2 mode */ 888 879 value |= 3 << 12; 889 880 else if (pcdev->is_16bit) 890 881 value |= 1 << 12; ··· 1063 1054 return 0; 1064 1055 } 1065 1056 1066 - if (!pcdev->pdata || !pcdev->pdata->csi2) { 1057 + if (!csi2_subdev(pcdev, icd)) { 1067 1058 /* Are there any restrictions in the CSI-2 case? */ 1068 1059 ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); 1069 1060 if (ret < 0) ··· 2093 2084 struct resource *res; 2094 2085 void __iomem *base; 2095 2086 unsigned int irq; 2096 - int err = 0; 2087 + int err, i; 2097 2088 struct bus_wait wait = { 2098 2089 .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion), 2099 2090 .notifier.notifier_call = bus_notify, ··· 2197 2188 goto exit_free_clk; 2198 2189 } 2199 2190 2200 - err = soc_camera_host_register(&pcdev->ici); 2201 - if (err) 2202 - goto exit_free_ctx; 2191 + if (pcdev->pdata && pcdev->pdata->asd_sizes) { 2192 + struct v4l2_async_subdev **asd; 2193 + char name[] = "sh-mobile-csi2"; 2194 + int j; 2203 2195 2204 - /* CSI2 interfacing */ 2196 + /* 2197 + * CSI2 interfacing: several groups can use CSI2, pick up the 2198 + * first one 2199 + */ 2200 + asd = pcdev->pdata->asd; 2201 + for (j = 0; pcdev->pdata->asd_sizes[j]; j++) { 2202 + for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) { 2203 + dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n", 2204 + __func__, i, (*asd)->bus_type); 2205 + if ((*asd)->bus_type == V4L2_ASYNC_BUS_PLATFORM && 2206 + !strncmp(name, (*asd)->match.platform.name, 2207 + sizeof(name) - 1)) { 2208 + pcdev->csi2_asd = *asd; 2209 + break; 2210 + } 2211 + } 2212 + if (pcdev->csi2_asd) 2213 + break; 2214 + } 2215 + 2216 + pcdev->ici.asd = pcdev->pdata->asd; 2217 + pcdev->ici.asd_sizes = pcdev->pdata->asd_sizes; 2218 + } 2219 + 2220 + /* Legacy CSI2 interfacing */ 2205 2221 csi2 = pcdev->pdata ? pcdev->pdata->csi2 : NULL; 2206 2222 if (csi2) { 2223 + /* 2224 + * TODO: remove this once all users are converted to 2225 + * asynchronous CSI2 probing. If it has to be kept, csi2 2226 + * platform device resources have to be added, using 2227 + * platform_device_add_resources() 2228 + */ 2207 2229 struct platform_device *csi2_pdev = 2208 2230 platform_device_alloc("sh-mobile-csi2", csi2->id); 2209 2231 struct sh_csi2_pdata *csi2_pdata = csi2->platform_data; 2210 2232 2211 2233 if (!csi2_pdev) { 2212 2234 err = -ENOMEM; 2213 - goto exit_host_unregister; 2235 + goto exit_free_ctx; 2214 2236 } 2215 2237 2216 2238 pcdev->csi2_pdev = csi2_pdev; 2217 2239 2218 - err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata)); 2240 + err = platform_device_add_data(csi2_pdev, csi2_pdata, 2241 + sizeof(*csi2_pdata)); 2219 2242 if (err < 0) 2220 2243 goto exit_pdev_put; 2221 - 2222 - csi2_pdata = csi2_pdev->dev.platform_data; 2223 - csi2_pdata->v4l2_dev = &pcdev->ici.v4l2_dev; 2224 2244 2225 2245 csi2_pdev->resource = csi2->resource; 2226 2246 csi2_pdev->num_resources = csi2->num_resources; ··· 2292 2254 err = -ENODEV; 2293 2255 goto exit_pdev_unregister; 2294 2256 } 2257 + 2258 + pcdev->csi2_sd = platform_get_drvdata(csi2_pdev); 2259 + } 2260 + 2261 + err = soc_camera_host_register(&pcdev->ici); 2262 + if (err) 2263 + goto exit_csi2_unregister; 2264 + 2265 + if (csi2) { 2266 + err = v4l2_device_register_subdev(&pcdev->ici.v4l2_dev, 2267 + pcdev->csi2_sd); 2268 + dev_dbg(&pdev->dev, "%s(): ret(register_subdev) = %d\n", 2269 + __func__, err); 2270 + if (err < 0) 2271 + goto exit_host_unregister; 2272 + /* v4l2_device_register_subdev() took a reference too */ 2273 + module_put(pcdev->csi2_sd->owner); 2295 2274 } 2296 2275 2297 2276 return 0; 2298 2277 2299 - exit_pdev_unregister: 2300 - platform_device_del(pcdev->csi2_pdev); 2301 - exit_pdev_put: 2302 - pcdev->csi2_pdev->resource = NULL; 2303 - platform_device_put(pcdev->csi2_pdev); 2304 2278 exit_host_unregister: 2305 2279 soc_camera_host_unregister(&pcdev->ici); 2280 + exit_csi2_unregister: 2281 + if (csi2) { 2282 + module_put(pcdev->csi2_pdev->dev.driver->owner); 2283 + exit_pdev_unregister: 2284 + platform_device_del(pcdev->csi2_pdev); 2285 + exit_pdev_put: 2286 + pcdev->csi2_pdev->resource = NULL; 2287 + platform_device_put(pcdev->csi2_pdev); 2288 + } 2306 2289 exit_free_ctx: 2307 2290 vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); 2308 2291 exit_free_clk: ··· 2383 2324 static struct platform_driver sh_mobile_ceu_driver = { 2384 2325 .driver = { 2385 2326 .name = "sh_mobile_ceu", 2327 + .owner = THIS_MODULE, 2386 2328 .pm = &sh_mobile_ceu_dev_pm_ops, 2387 2329 .of_match_table = sh_mobile_ceu_of_match, 2388 2330 }, ··· 2409 2349 MODULE_DESCRIPTION("SuperH Mobile CEU driver"); 2410 2350 MODULE_AUTHOR("Magnus Damm"); 2411 2351 MODULE_LICENSE("GPL"); 2412 - MODULE_VERSION("0.0.6"); 2352 + MODULE_VERSION("0.1.0"); 2413 2353 MODULE_ALIAS("platform:sh_mobile_ceu");
+90 -63
drivers/media/platform/soc_camera/sh_mobile_csi2.c
··· 36 36 37 37 struct sh_csi2 { 38 38 struct v4l2_subdev subdev; 39 - struct list_head list; 40 39 unsigned int irq; 41 40 unsigned long mipi_flags; 42 41 void __iomem *base; 43 42 struct platform_device *pdev; 44 43 struct sh_csi2_client_config *client; 45 44 }; 45 + 46 + static void sh_csi2_hwinit(struct sh_csi2 *priv); 46 47 47 48 static int sh_csi2_try_fmt(struct v4l2_subdev *sd, 48 49 struct v4l2_mbus_framefmt *mf) ··· 133 132 static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd, 134 133 struct v4l2_mbus_config *cfg) 135 134 { 136 - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | 137 - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | 138 - V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH; 139 - cfg->type = V4L2_MBUS_PARALLEL; 135 + struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); 136 + 137 + if (!priv->mipi_flags) { 138 + struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd); 139 + struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd); 140 + struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; 141 + unsigned long common_flags, csi2_flags; 142 + struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,}; 143 + int ret; 144 + 145 + /* Check if we can support this camera */ 146 + csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | 147 + V4L2_MBUS_CSI2_1_LANE; 148 + 149 + switch (pdata->type) { 150 + case SH_CSI2C: 151 + if (priv->client->lanes != 1) 152 + csi2_flags |= V4L2_MBUS_CSI2_2_LANE; 153 + break; 154 + case SH_CSI2I: 155 + switch (priv->client->lanes) { 156 + default: 157 + csi2_flags |= V4L2_MBUS_CSI2_4_LANE; 158 + case 3: 159 + csi2_flags |= V4L2_MBUS_CSI2_3_LANE; 160 + case 2: 161 + csi2_flags |= V4L2_MBUS_CSI2_2_LANE; 162 + } 163 + } 164 + 165 + ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &client_cfg); 166 + if (ret == -ENOIOCTLCMD) 167 + common_flags = csi2_flags; 168 + else if (!ret) 169 + common_flags = soc_mbus_config_compatible(&client_cfg, 170 + csi2_flags); 171 + else 172 + common_flags = 0; 173 + 174 + if (!common_flags) 175 + return -EINVAL; 176 + 177 + /* All good: camera MIPI configuration supported */ 178 + priv->mipi_flags = common_flags; 179 + } 180 + 181 + if (cfg) { 182 + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | 183 + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | 184 + V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH; 185 + cfg->type = V4L2_MBUS_PARALLEL; 186 + } 140 187 141 188 return 0; 142 189 } ··· 195 146 struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); 196 147 struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd); 197 148 struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd); 198 - struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2, 199 - .flags = priv->mipi_flags}; 149 + struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,}; 150 + int ret = sh_csi2_g_mbus_config(sd, NULL); 151 + 152 + if (ret < 0) 153 + return ret; 154 + 155 + pm_runtime_get_sync(&priv->pdev->dev); 156 + 157 + sh_csi2_hwinit(priv); 158 + 159 + client_cfg.flags = priv->mipi_flags; 200 160 201 161 return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg); 202 162 } ··· 260 202 261 203 static int sh_csi2_client_connect(struct sh_csi2 *priv) 262 204 { 263 - struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; 264 - struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev); 265 - struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd); 266 205 struct device *dev = v4l2_get_subdevdata(&priv->subdev); 267 - struct v4l2_mbus_config cfg; 268 - unsigned long common_flags, csi2_flags; 269 - int i, ret; 206 + struct sh_csi2_pdata *pdata = dev->platform_data; 207 + struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev); 208 + int i; 270 209 271 210 if (priv->client) 272 211 return -EBUSY; 273 212 274 213 for (i = 0; i < pdata->num_clients; i++) 275 - if (&pdata->clients[i].pdev->dev == icd->pdev) 214 + if ((pdata->clients[i].pdev && 215 + &pdata->clients[i].pdev->dev == icd->pdev) || 216 + (icd->control && 217 + strcmp(pdata->clients[i].name, dev_name(icd->control)))) 276 218 break; 277 219 278 220 dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i); ··· 280 222 if (i == pdata->num_clients) 281 223 return -ENODEV; 282 224 283 - /* Check if we can support this camera */ 284 - csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE; 285 - 286 - switch (pdata->type) { 287 - case SH_CSI2C: 288 - if (pdata->clients[i].lanes != 1) 289 - csi2_flags |= V4L2_MBUS_CSI2_2_LANE; 290 - break; 291 - case SH_CSI2I: 292 - switch (pdata->clients[i].lanes) { 293 - default: 294 - csi2_flags |= V4L2_MBUS_CSI2_4_LANE; 295 - case 3: 296 - csi2_flags |= V4L2_MBUS_CSI2_3_LANE; 297 - case 2: 298 - csi2_flags |= V4L2_MBUS_CSI2_2_LANE; 299 - } 300 - } 301 - 302 - cfg.type = V4L2_MBUS_CSI2; 303 - ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg); 304 - if (ret == -ENOIOCTLCMD) 305 - common_flags = csi2_flags; 306 - else if (!ret) 307 - common_flags = soc_mbus_config_compatible(&cfg, 308 - csi2_flags); 309 - else 310 - common_flags = 0; 311 - 312 - if (!common_flags) 313 - return -EINVAL; 314 - 315 - /* All good: camera MIPI configuration supported */ 316 - priv->mipi_flags = common_flags; 317 225 priv->client = pdata->clients + i; 318 - 319 - pm_runtime_get_sync(dev); 320 - 321 - sh_csi2_hwinit(priv); 322 226 323 227 return 0; 324 228 } ··· 324 304 /* Platform data specify the PHY, lanes, ECC, CRC */ 325 305 struct sh_csi2_pdata *pdata = pdev->dev.platform_data; 326 306 307 + if (!pdata) 308 + return -EINVAL; 309 + 310 + priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL); 311 + if (!priv) 312 + return -ENOMEM; 313 + 327 314 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 328 315 /* Interrupt unused so far */ 329 316 irq = platform_get_irq(pdev, 0); 330 317 331 - if (!res || (int)irq <= 0 || !pdata) { 318 + if (!res || (int)irq <= 0) { 332 319 dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n"); 333 320 return -ENODEV; 334 321 } ··· 346 319 return -EINVAL; 347 320 } 348 321 349 - priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL); 350 - if (!priv) 351 - return -ENOMEM; 352 - 353 322 priv->irq = irq; 354 323 355 324 priv->base = devm_ioremap_resource(&pdev->dev, res); ··· 353 330 return PTR_ERR(priv->base); 354 331 355 332 priv->pdev = pdev; 356 - platform_set_drvdata(pdev, priv); 333 + priv->subdev.owner = THIS_MODULE; 334 + priv->subdev.dev = &pdev->dev; 335 + platform_set_drvdata(pdev, &priv->subdev); 357 336 358 337 v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops); 359 338 v4l2_set_subdevdata(&priv->subdev, &pdev->dev); 360 339 361 340 snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi", 362 - dev_name(pdata->v4l2_dev->dev)); 363 - ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev); 364 - dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret); 341 + dev_name(&pdev->dev)); 342 + 343 + ret = v4l2_async_register_subdev(&priv->subdev); 365 344 if (ret < 0) 366 345 return ret; 367 346 ··· 376 351 377 352 static int sh_csi2_remove(struct platform_device *pdev) 378 353 { 379 - struct sh_csi2 *priv = platform_get_drvdata(pdev); 354 + struct v4l2_subdev *subdev = platform_get_drvdata(pdev); 355 + struct sh_csi2 *priv = container_of(subdev, struct sh_csi2, subdev); 380 356 381 - v4l2_device_unregister_subdev(&priv->subdev); 357 + v4l2_async_unregister_subdev(&priv->subdev); 358 + v4l2_device_unregister_subdev(subdev); 382 359 pm_runtime_disable(&pdev->dev); 383 360 384 361 return 0;
+2
include/media/sh_mobile_ceu.h
··· 22 22 int max_width; 23 23 int max_height; 24 24 struct sh_mobile_ceu_companion *csi2; 25 + struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ 26 + int *asd_sizes; /* 0-terminated array pf asd group sizes */ 25 27 }; 26 28 27 29 #endif /* __ASM_SH_MOBILE_CEU_H__ */
+1 -1
include/media/sh_mobile_csi2.h
··· 33 33 unsigned char lanes; /* bitmask[3:0] */ 34 34 unsigned char channel; /* 0..3 */ 35 35 struct platform_device *pdev; /* client platform device */ 36 + const char *name; /* async matching: client name */ 36 37 }; 37 38 38 39 struct v4l2_device; ··· 43 42 unsigned int flags; 44 43 struct sh_csi2_client_config *clients; 45 44 int num_clients; 46 - struct v4l2_device *v4l2_dev; 47 45 }; 48 46 49 47 #endif