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