7e3b9da5aa11ef215cc20cfc9f80948709c28cf7
[deliverable/linux.git] / drivers / media / platform / vsp1 / vsp1_drv.c
1 /*
2 * vsp1_drv.c -- R-Car VSP1 Driver
3 *
4 * Copyright (C) 2013-2015 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/clk.h>
15 #include <linux/delay.h>
16 #include <linux/device.h>
17 #include <linux/interrupt.h>
18 #include <linux/module.h>
19 #include <linux/of.h>
20 #include <linux/of_device.h>
21 #include <linux/platform_device.h>
22 #include <linux/pm_runtime.h>
23 #include <linux/videodev2.h>
24
25 #include <media/rcar-fcp.h>
26 #include <media/v4l2-subdev.h>
27
28 #include "vsp1.h"
29 #include "vsp1_bru.h"
30 #include "vsp1_dl.h"
31 #include "vsp1_drm.h"
32 #include "vsp1_hsit.h"
33 #include "vsp1_lif.h"
34 #include "vsp1_lut.h"
35 #include "vsp1_pipe.h"
36 #include "vsp1_rwpf.h"
37 #include "vsp1_sru.h"
38 #include "vsp1_uds.h"
39 #include "vsp1_video.h"
40
41 /* -----------------------------------------------------------------------------
42 * Interrupt Handling
43 */
44
45 static irqreturn_t vsp1_irq_handler(int irq, void *data)
46 {
47 u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE;
48 struct vsp1_device *vsp1 = data;
49 irqreturn_t ret = IRQ_NONE;
50 unsigned int i;
51 u32 status;
52
53 for (i = 0; i < vsp1->info->wpf_count; ++i) {
54 struct vsp1_rwpf *wpf = vsp1->wpf[i];
55
56 if (wpf == NULL)
57 continue;
58
59 status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i));
60 vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask);
61
62 if (status & VI6_WFP_IRQ_STA_FRE) {
63 vsp1_pipeline_frame_end(wpf->pipe);
64 ret = IRQ_HANDLED;
65 }
66 }
67
68 status = vsp1_read(vsp1, VI6_DISP_IRQ_STA);
69 vsp1_write(vsp1, VI6_DISP_IRQ_STA, ~status & VI6_DISP_IRQ_STA_DST);
70
71 if (status & VI6_DISP_IRQ_STA_DST) {
72 vsp1_drm_display_start(vsp1);
73 ret = IRQ_HANDLED;
74 }
75
76 return ret;
77 }
78
79 /* -----------------------------------------------------------------------------
80 * Entities
81 */
82
83 /*
84 * vsp1_create_sink_links - Create links from all sources to the given sink
85 *
86 * This function creates media links from all valid sources to the given sink
87 * pad. Links that would be invalid according to the VSP1 hardware capabilities
88 * are skipped. Those include all links
89 *
90 * - from a UDS to a UDS (UDS entities can't be chained)
91 * - from an entity to itself (no loops are allowed)
92 */
93 static int vsp1_create_sink_links(struct vsp1_device *vsp1,
94 struct vsp1_entity *sink)
95 {
96 struct media_entity *entity = &sink->subdev.entity;
97 struct vsp1_entity *source;
98 unsigned int pad;
99 int ret;
100
101 list_for_each_entry(source, &vsp1->entities, list_dev) {
102 u32 flags;
103
104 if (source->type == sink->type)
105 continue;
106
107 if (source->type == VSP1_ENTITY_LIF ||
108 source->type == VSP1_ENTITY_WPF)
109 continue;
110
111 flags = source->type == VSP1_ENTITY_RPF &&
112 sink->type == VSP1_ENTITY_WPF &&
113 source->index == sink->index
114 ? MEDIA_LNK_FL_ENABLED : 0;
115
116 for (pad = 0; pad < entity->num_pads; ++pad) {
117 if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK))
118 continue;
119
120 ret = media_create_pad_link(&source->subdev.entity,
121 source->source_pad,
122 entity, pad, flags);
123 if (ret < 0)
124 return ret;
125
126 if (flags & MEDIA_LNK_FL_ENABLED)
127 source->sink = entity;
128 }
129 }
130
131 return 0;
132 }
133
134 static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
135 {
136 struct vsp1_entity *entity;
137 unsigned int i;
138 int ret;
139
140 list_for_each_entry(entity, &vsp1->entities, list_dev) {
141 if (entity->type == VSP1_ENTITY_LIF ||
142 entity->type == VSP1_ENTITY_RPF)
143 continue;
144
145 ret = vsp1_create_sink_links(vsp1, entity);
146 if (ret < 0)
147 return ret;
148 }
149
150 if (vsp1->lif) {
151 ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity,
152 RWPF_PAD_SOURCE,
153 &vsp1->lif->entity.subdev.entity,
154 LIF_PAD_SINK, 0);
155 if (ret < 0)
156 return ret;
157 }
158
159 for (i = 0; i < vsp1->info->rpf_count; ++i) {
160 struct vsp1_rwpf *rpf = vsp1->rpf[i];
161
162 ret = media_create_pad_link(&rpf->video->video.entity, 0,
163 &rpf->entity.subdev.entity,
164 RWPF_PAD_SINK,
165 MEDIA_LNK_FL_ENABLED |
166 MEDIA_LNK_FL_IMMUTABLE);
167 if (ret < 0)
168 return ret;
169 }
170
171 for (i = 0; i < vsp1->info->wpf_count; ++i) {
172 /* Connect the video device to the WPF. All connections are
173 * immutable except for the WPF0 source link if a LIF is
174 * present.
175 */
176 struct vsp1_rwpf *wpf = vsp1->wpf[i];
177 unsigned int flags = MEDIA_LNK_FL_ENABLED;
178
179 if (!(vsp1->info->features & VSP1_HAS_LIF) || i != 0)
180 flags |= MEDIA_LNK_FL_IMMUTABLE;
181
182 ret = media_create_pad_link(&wpf->entity.subdev.entity,
183 RWPF_PAD_SOURCE,
184 &wpf->video->video.entity, 0,
185 flags);
186 if (ret < 0)
187 return ret;
188 }
189
190 return 0;
191 }
192
193 static void vsp1_destroy_entities(struct vsp1_device *vsp1)
194 {
195 struct vsp1_entity *entity, *_entity;
196 struct vsp1_video *video, *_video;
197
198 list_for_each_entry_safe(entity, _entity, &vsp1->entities, list_dev) {
199 list_del(&entity->list_dev);
200 vsp1_entity_destroy(entity);
201 }
202
203 list_for_each_entry_safe(video, _video, &vsp1->videos, list) {
204 list_del(&video->list);
205 vsp1_video_cleanup(video);
206 }
207
208 v4l2_device_unregister(&vsp1->v4l2_dev);
209 media_device_unregister(&vsp1->media_dev);
210 media_device_cleanup(&vsp1->media_dev);
211
212 if (!vsp1->info->uapi)
213 vsp1_drm_cleanup(vsp1);
214 }
215
216 static int vsp1_create_entities(struct vsp1_device *vsp1)
217 {
218 struct media_device *mdev = &vsp1->media_dev;
219 struct v4l2_device *vdev = &vsp1->v4l2_dev;
220 struct vsp1_entity *entity;
221 unsigned int i;
222 int ret;
223
224 mdev->dev = vsp1->dev;
225 strlcpy(mdev->model, "VSP1", sizeof(mdev->model));
226 snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
227 dev_name(mdev->dev));
228 media_device_init(mdev);
229
230 vsp1->media_ops.link_setup = vsp1_entity_link_setup;
231 /* Don't perform link validation when the userspace API is disabled as
232 * the pipeline is configured internally by the driver in that case, and
233 * its configuration can thus be trusted.
234 */
235 if (vsp1->info->uapi)
236 vsp1->media_ops.link_validate = v4l2_subdev_link_validate;
237
238 vdev->mdev = mdev;
239 ret = v4l2_device_register(vsp1->dev, vdev);
240 if (ret < 0) {
241 dev_err(vsp1->dev, "V4L2 device registration failed (%d)\n",
242 ret);
243 goto done;
244 }
245
246 /* Instantiate all the entities. */
247 if (vsp1->info->features & VSP1_HAS_BRU) {
248 vsp1->bru = vsp1_bru_create(vsp1);
249 if (IS_ERR(vsp1->bru)) {
250 ret = PTR_ERR(vsp1->bru);
251 goto done;
252 }
253
254 list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);
255 }
256
257 vsp1->hsi = vsp1_hsit_create(vsp1, true);
258 if (IS_ERR(vsp1->hsi)) {
259 ret = PTR_ERR(vsp1->hsi);
260 goto done;
261 }
262
263 list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities);
264
265 vsp1->hst = vsp1_hsit_create(vsp1, false);
266 if (IS_ERR(vsp1->hst)) {
267 ret = PTR_ERR(vsp1->hst);
268 goto done;
269 }
270
271 list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
272
273 if (vsp1->info->features & VSP1_HAS_LIF) {
274 vsp1->lif = vsp1_lif_create(vsp1);
275 if (IS_ERR(vsp1->lif)) {
276 ret = PTR_ERR(vsp1->lif);
277 goto done;
278 }
279
280 list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities);
281 }
282
283 if (vsp1->info->features & VSP1_HAS_LUT) {
284 vsp1->lut = vsp1_lut_create(vsp1);
285 if (IS_ERR(vsp1->lut)) {
286 ret = PTR_ERR(vsp1->lut);
287 goto done;
288 }
289
290 list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities);
291 }
292
293 for (i = 0; i < vsp1->info->rpf_count; ++i) {
294 struct vsp1_rwpf *rpf;
295
296 rpf = vsp1_rpf_create(vsp1, i);
297 if (IS_ERR(rpf)) {
298 ret = PTR_ERR(rpf);
299 goto done;
300 }
301
302 vsp1->rpf[i] = rpf;
303 list_add_tail(&rpf->entity.list_dev, &vsp1->entities);
304
305 if (vsp1->info->uapi) {
306 struct vsp1_video *video = vsp1_video_create(vsp1, rpf);
307
308 if (IS_ERR(video)) {
309 ret = PTR_ERR(video);
310 goto done;
311 }
312
313 list_add_tail(&video->list, &vsp1->videos);
314 }
315 }
316
317 if (vsp1->info->features & VSP1_HAS_SRU) {
318 vsp1->sru = vsp1_sru_create(vsp1);
319 if (IS_ERR(vsp1->sru)) {
320 ret = PTR_ERR(vsp1->sru);
321 goto done;
322 }
323
324 list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities);
325 }
326
327 for (i = 0; i < vsp1->info->uds_count; ++i) {
328 struct vsp1_uds *uds;
329
330 uds = vsp1_uds_create(vsp1, i);
331 if (IS_ERR(uds)) {
332 ret = PTR_ERR(uds);
333 goto done;
334 }
335
336 vsp1->uds[i] = uds;
337 list_add_tail(&uds->entity.list_dev, &vsp1->entities);
338 }
339
340 for (i = 0; i < vsp1->info->wpf_count; ++i) {
341 struct vsp1_rwpf *wpf;
342
343 wpf = vsp1_wpf_create(vsp1, i);
344 if (IS_ERR(wpf)) {
345 ret = PTR_ERR(wpf);
346 goto done;
347 }
348
349 vsp1->wpf[i] = wpf;
350 list_add_tail(&wpf->entity.list_dev, &vsp1->entities);
351
352 if (vsp1->info->uapi) {
353 struct vsp1_video *video = vsp1_video_create(vsp1, wpf);
354
355 if (IS_ERR(video)) {
356 ret = PTR_ERR(video);
357 goto done;
358 }
359
360 list_add_tail(&video->list, &vsp1->videos);
361 wpf->entity.sink = &video->video.entity;
362 }
363 }
364
365 /* Register all subdevs. */
366 list_for_each_entry(entity, &vsp1->entities, list_dev) {
367 ret = v4l2_device_register_subdev(&vsp1->v4l2_dev,
368 &entity->subdev);
369 if (ret < 0)
370 goto done;
371 }
372
373 /* Create links. */
374 if (vsp1->info->uapi)
375 ret = vsp1_uapi_create_links(vsp1);
376 else
377 ret = vsp1_drm_create_links(vsp1);
378 if (ret < 0)
379 goto done;
380
381 /* Register subdev nodes if the userspace API is enabled or initialize
382 * the DRM pipeline otherwise.
383 */
384 if (vsp1->info->uapi)
385 ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
386 else
387 ret = vsp1_drm_init(vsp1);
388 if (ret < 0)
389 goto done;
390
391 ret = media_device_register(mdev);
392
393 done:
394 if (ret < 0)
395 vsp1_destroy_entities(vsp1);
396
397 return ret;
398 }
399
400 int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index)
401 {
402 unsigned int timeout;
403 u32 status;
404
405 status = vsp1_read(vsp1, VI6_STATUS);
406 if (!(status & VI6_STATUS_SYS_ACT(index)))
407 return 0;
408
409 vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(index));
410 for (timeout = 10; timeout > 0; --timeout) {
411 status = vsp1_read(vsp1, VI6_STATUS);
412 if (!(status & VI6_STATUS_SYS_ACT(index)))
413 break;
414
415 usleep_range(1000, 2000);
416 }
417
418 if (!timeout) {
419 dev_err(vsp1->dev, "failed to reset wpf.%u\n", index);
420 return -ETIMEDOUT;
421 }
422
423 return 0;
424 }
425
426 static int vsp1_device_init(struct vsp1_device *vsp1)
427 {
428 unsigned int i;
429 int ret;
430
431 /* Reset any channel that might be running. */
432 for (i = 0; i < vsp1->info->wpf_count; ++i) {
433 ret = vsp1_reset_wpf(vsp1, i);
434 if (ret < 0)
435 return ret;
436 }
437
438 vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) |
439 (8 << VI6_CLK_DCSWT_CSTRW_SHIFT));
440
441 for (i = 0; i < vsp1->info->rpf_count; ++i)
442 vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED);
443
444 for (i = 0; i < vsp1->info->uds_count; ++i)
445 vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);
446
447 vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);
448 vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED);
449 vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED);
450 vsp1_write(vsp1, VI6_DPR_HST_ROUTE, VI6_DPR_NODE_UNUSED);
451 vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED);
452 vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED);
453
454 vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
455 (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
456 vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
457 (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
458
459 vsp1_dlm_setup(vsp1);
460
461 return 0;
462 }
463
464 /*
465 * vsp1_device_get - Acquire the VSP1 device
466 *
467 * Make sure the device is not suspended and initialize it if needed.
468 *
469 * Return 0 on success or a negative error code otherwise.
470 */
471 int vsp1_device_get(struct vsp1_device *vsp1)
472 {
473 int ret;
474
475 ret = pm_runtime_get_sync(vsp1->dev);
476 return ret < 0 ? ret : 0;
477 }
478
479 /*
480 * vsp1_device_put - Release the VSP1 device
481 *
482 * Decrement the VSP1 reference count and cleanup the device if the last
483 * reference is released.
484 */
485 void vsp1_device_put(struct vsp1_device *vsp1)
486 {
487 pm_runtime_put_sync(vsp1->dev);
488 }
489
490 /* -----------------------------------------------------------------------------
491 * Power Management
492 */
493
494 #ifdef CONFIG_PM_SLEEP
495 static int vsp1_pm_suspend(struct device *dev)
496 {
497 struct vsp1_device *vsp1 = dev_get_drvdata(dev);
498
499 vsp1_pipelines_suspend(vsp1);
500 pm_runtime_force_suspend(vsp1->dev);
501
502 return 0;
503 }
504
505 static int vsp1_pm_resume(struct device *dev)
506 {
507 struct vsp1_device *vsp1 = dev_get_drvdata(dev);
508
509 pm_runtime_force_resume(vsp1->dev);
510 vsp1_pipelines_resume(vsp1);
511
512 return 0;
513 }
514 #endif
515
516 static int vsp1_pm_runtime_suspend(struct device *dev)
517 {
518 struct vsp1_device *vsp1 = dev_get_drvdata(dev);
519
520 rcar_fcp_disable(vsp1->fcp);
521
522 return 0;
523 }
524
525 static int vsp1_pm_runtime_resume(struct device *dev)
526 {
527 struct vsp1_device *vsp1 = dev_get_drvdata(dev);
528 int ret;
529
530 if (vsp1->info) {
531 ret = vsp1_device_init(vsp1);
532 if (ret < 0)
533 return ret;
534 }
535
536 return rcar_fcp_enable(vsp1->fcp);
537 }
538
539 static const struct dev_pm_ops vsp1_pm_ops = {
540 SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume)
541 SET_RUNTIME_PM_OPS(vsp1_pm_runtime_suspend, vsp1_pm_runtime_resume, NULL)
542 };
543
544 /* -----------------------------------------------------------------------------
545 * Platform Driver
546 */
547
548 static const struct vsp1_device_info vsp1_device_infos[] = {
549 {
550 .version = VI6_IP_VERSION_MODEL_VSPS_H2,
551 .gen = 2,
552 .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
553 .rpf_count = 5,
554 .uds_count = 3,
555 .wpf_count = 4,
556 .num_bru_inputs = 4,
557 .uapi = true,
558 }, {
559 .version = VI6_IP_VERSION_MODEL_VSPR_H2,
560 .gen = 2,
561 .features = VSP1_HAS_BRU | VSP1_HAS_SRU,
562 .rpf_count = 5,
563 .uds_count = 3,
564 .wpf_count = 4,
565 .num_bru_inputs = 4,
566 .uapi = true,
567 }, {
568 .version = VI6_IP_VERSION_MODEL_VSPD_GEN2,
569 .gen = 2,
570 .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT,
571 .rpf_count = 4,
572 .uds_count = 1,
573 .wpf_count = 1,
574 .num_bru_inputs = 4,
575 .uapi = true,
576 }, {
577 .version = VI6_IP_VERSION_MODEL_VSPS_M2,
578 .gen = 2,
579 .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
580 .rpf_count = 5,
581 .uds_count = 1,
582 .wpf_count = 4,
583 .num_bru_inputs = 4,
584 .uapi = true,
585 }, {
586 .version = VI6_IP_VERSION_MODEL_VSPI_GEN3,
587 .gen = 3,
588 .features = VSP1_HAS_LUT | VSP1_HAS_SRU,
589 .rpf_count = 1,
590 .uds_count = 1,
591 .wpf_count = 1,
592 .uapi = true,
593 }, {
594 .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3,
595 .gen = 3,
596 .features = VSP1_HAS_BRU,
597 .rpf_count = 5,
598 .wpf_count = 1,
599 .num_bru_inputs = 5,
600 .uapi = true,
601 }, {
602 .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3,
603 .gen = 3,
604 .features = VSP1_HAS_BRU | VSP1_HAS_LUT,
605 .rpf_count = 5,
606 .wpf_count = 1,
607 .num_bru_inputs = 5,
608 .uapi = true,
609 }, {
610 .version = VI6_IP_VERSION_MODEL_VSPD_GEN3,
611 .gen = 3,
612 .features = VSP1_HAS_BRU | VSP1_HAS_LIF,
613 .rpf_count = 5,
614 .wpf_count = 2,
615 .num_bru_inputs = 5,
616 },
617 };
618
619 static int vsp1_probe(struct platform_device *pdev)
620 {
621 struct vsp1_device *vsp1;
622 struct device_node *fcp_node;
623 struct resource *irq;
624 struct resource *io;
625 unsigned int i;
626 u32 version;
627 int ret;
628
629 vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL);
630 if (vsp1 == NULL)
631 return -ENOMEM;
632
633 vsp1->dev = &pdev->dev;
634 INIT_LIST_HEAD(&vsp1->entities);
635 INIT_LIST_HEAD(&vsp1->videos);
636
637 platform_set_drvdata(pdev, vsp1);
638
639 /* I/O and IRQ resources (clock managed by the clock PM domain) */
640 io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
641 vsp1->mmio = devm_ioremap_resource(&pdev->dev, io);
642 if (IS_ERR(vsp1->mmio))
643 return PTR_ERR(vsp1->mmio);
644
645 irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
646 if (!irq) {
647 dev_err(&pdev->dev, "missing IRQ\n");
648 return -EINVAL;
649 }
650
651 ret = devm_request_irq(&pdev->dev, irq->start, vsp1_irq_handler,
652 IRQF_SHARED, dev_name(&pdev->dev), vsp1);
653 if (ret < 0) {
654 dev_err(&pdev->dev, "failed to request IRQ\n");
655 return ret;
656 }
657
658 /* FCP (optional) */
659 fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0);
660 if (fcp_node) {
661 vsp1->fcp = rcar_fcp_get(fcp_node);
662 of_node_put(fcp_node);
663 if (IS_ERR(vsp1->fcp)) {
664 dev_dbg(&pdev->dev, "FCP not found (%ld)\n",
665 PTR_ERR(vsp1->fcp));
666 return PTR_ERR(vsp1->fcp);
667 }
668 }
669
670 /* Configure device parameters based on the version register. */
671 pm_runtime_enable(&pdev->dev);
672
673 ret = pm_runtime_get_sync(&pdev->dev);
674 if (ret < 0)
675 goto done;
676
677 version = vsp1_read(vsp1, VI6_IP_VERSION);
678 pm_runtime_put_sync(&pdev->dev);
679
680 for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) {
681 if ((version & VI6_IP_VERSION_MODEL_MASK) ==
682 vsp1_device_infos[i].version) {
683 vsp1->info = &vsp1_device_infos[i];
684 break;
685 }
686 }
687
688 if (!vsp1->info) {
689 dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version);
690 ret = -ENXIO;
691 goto done;
692 }
693
694 dev_dbg(&pdev->dev, "IP version 0x%08x\n", version);
695
696 /* Instanciate entities */
697 ret = vsp1_create_entities(vsp1);
698 if (ret < 0) {
699 dev_err(&pdev->dev, "failed to create entities\n");
700 goto done;
701 }
702
703 done:
704 if (ret)
705 pm_runtime_disable(&pdev->dev);
706
707 return ret;
708 }
709
710 static int vsp1_remove(struct platform_device *pdev)
711 {
712 struct vsp1_device *vsp1 = platform_get_drvdata(pdev);
713
714 vsp1_destroy_entities(vsp1);
715 rcar_fcp_put(vsp1->fcp);
716
717 pm_runtime_disable(&pdev->dev);
718
719 return 0;
720 }
721
722 static const struct of_device_id vsp1_of_match[] = {
723 { .compatible = "renesas,vsp1" },
724 { .compatible = "renesas,vsp2" },
725 { },
726 };
727
728 static struct platform_driver vsp1_platform_driver = {
729 .probe = vsp1_probe,
730 .remove = vsp1_remove,
731 .driver = {
732 .name = "vsp1",
733 .pm = &vsp1_pm_ops,
734 .of_match_table = vsp1_of_match,
735 },
736 };
737
738 module_platform_driver(vsp1_platform_driver);
739
740 MODULE_ALIAS("vsp1");
741 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
742 MODULE_DESCRIPTION("Renesas VSP1 Driver");
743 MODULE_LICENSE("GPL");
This page took 0.045724 seconds and 4 git commands to generate.