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; | |
223 | strlcpy(mdev->model, "VSP1", sizeof(mdev->model)); | |
10d79b99 LP |
224 | snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", |
225 | dev_name(mdev->dev)); | |
9832e155 | 226 | media_device_init(mdev); |
26e0ca22 | 227 | |
babca007 LP |
228 | vsp1->media_ops.link_setup = vsp1_entity_link_setup; |
229 | /* Don't perform link validation when the userspace API is disabled as | |
230 | * the pipeline is configured internally by the driver in that case, and | |
231 | * its configuration can thus be trusted. | |
232 | */ | |
5aa2eb3c | 233 | if (vsp1->info->uapi) |
babca007 LP |
234 | vsp1->media_ops.link_validate = v4l2_subdev_link_validate; |
235 | ||
26e0ca22 LP |
236 | vdev->mdev = mdev; |
237 | ret = v4l2_device_register(vsp1->dev, vdev); | |
238 | if (ret < 0) { | |
239 | dev_err(vsp1->dev, "V4L2 device registration failed (%d)\n", | |
240 | ret); | |
241 | goto done; | |
242 | } | |
243 | ||
244 | /* Instantiate all the entities. */ | |
5aa2eb3c | 245 | if (vsp1->info->features & VSP1_HAS_BRU) { |
f74be412 LP |
246 | vsp1->bru = vsp1_bru_create(vsp1); |
247 | if (IS_ERR(vsp1->bru)) { | |
248 | ret = PTR_ERR(vsp1->bru); | |
249 | goto done; | |
250 | } | |
629bb6d4 | 251 | |
f74be412 LP |
252 | list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities); |
253 | } | |
629bb6d4 | 254 | |
1fd87bf2 LP |
255 | if (vsp1->info->features & VSP1_HAS_CLU) { |
256 | vsp1->clu = vsp1_clu_create(vsp1); | |
257 | if (IS_ERR(vsp1->clu)) { | |
258 | ret = PTR_ERR(vsp1->clu); | |
259 | goto done; | |
260 | } | |
261 | ||
262 | list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities); | |
263 | } | |
264 | ||
5cdf5741 LP |
265 | vsp1->hsi = vsp1_hsit_create(vsp1, true); |
266 | if (IS_ERR(vsp1->hsi)) { | |
267 | ret = PTR_ERR(vsp1->hsi); | |
268 | goto done; | |
269 | } | |
270 | ||
271 | list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities); | |
272 | ||
273 | vsp1->hst = vsp1_hsit_create(vsp1, false); | |
274 | if (IS_ERR(vsp1->hst)) { | |
275 | ret = PTR_ERR(vsp1->hst); | |
276 | goto done; | |
277 | } | |
278 | ||
279 | list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); | |
280 | ||
8d954abe LP |
281 | /* The LIF is only supported when used in conjunction with the DU, in |
282 | * which case the userspace API is disabled. If the userspace API is | |
283 | * enabled skip the LIF, even when present. | |
284 | */ | |
285 | if (vsp1->info->features & VSP1_HAS_LIF && !vsp1->info->uapi) { | |
26e0ca22 LP |
286 | vsp1->lif = vsp1_lif_create(vsp1); |
287 | if (IS_ERR(vsp1->lif)) { | |
288 | ret = PTR_ERR(vsp1->lif); | |
289 | goto done; | |
290 | } | |
291 | ||
292 | list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities); | |
293 | } | |
294 | ||
5aa2eb3c | 295 | if (vsp1->info->features & VSP1_HAS_LUT) { |
989af883 LP |
296 | vsp1->lut = vsp1_lut_create(vsp1); |
297 | if (IS_ERR(vsp1->lut)) { | |
298 | ret = PTR_ERR(vsp1->lut); | |
299 | goto done; | |
300 | } | |
301 | ||
302 | list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities); | |
303 | } | |
304 | ||
5aa2eb3c | 305 | for (i = 0; i < vsp1->info->rpf_count; ++i) { |
26e0ca22 LP |
306 | struct vsp1_rwpf *rpf; |
307 | ||
308 | rpf = vsp1_rpf_create(vsp1, i); | |
309 | if (IS_ERR(rpf)) { | |
310 | ret = PTR_ERR(rpf); | |
311 | goto done; | |
312 | } | |
313 | ||
314 | vsp1->rpf[i] = rpf; | |
315 | list_add_tail(&rpf->entity.list_dev, &vsp1->entities); | |
9d40637a | 316 | |
5aa2eb3c | 317 | if (vsp1->info->uapi) { |
f2ed459d | 318 | struct vsp1_video *video = vsp1_video_create(vsp1, rpf); |
9d40637a | 319 | |
f2ed459d LP |
320 | if (IS_ERR(video)) { |
321 | ret = PTR_ERR(video); | |
322 | goto done; | |
323 | } | |
324 | ||
325 | list_add_tail(&video->list, &vsp1->videos); | |
326 | } | |
26e0ca22 LP |
327 | } |
328 | ||
5aa2eb3c | 329 | if (vsp1->info->features & VSP1_HAS_SRU) { |
a626e64e LP |
330 | vsp1->sru = vsp1_sru_create(vsp1); |
331 | if (IS_ERR(vsp1->sru)) { | |
332 | ret = PTR_ERR(vsp1->sru); | |
333 | goto done; | |
334 | } | |
335 | ||
336 | list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities); | |
337 | } | |
338 | ||
5aa2eb3c | 339 | for (i = 0; i < vsp1->info->uds_count; ++i) { |
26e0ca22 LP |
340 | struct vsp1_uds *uds; |
341 | ||
342 | uds = vsp1_uds_create(vsp1, i); | |
343 | if (IS_ERR(uds)) { | |
344 | ret = PTR_ERR(uds); | |
345 | goto done; | |
346 | } | |
347 | ||
348 | vsp1->uds[i] = uds; | |
349 | list_add_tail(&uds->entity.list_dev, &vsp1->entities); | |
350 | } | |
351 | ||
5aa2eb3c | 352 | for (i = 0; i < vsp1->info->wpf_count; ++i) { |
26e0ca22 LP |
353 | struct vsp1_rwpf *wpf; |
354 | ||
355 | wpf = vsp1_wpf_create(vsp1, i); | |
356 | if (IS_ERR(wpf)) { | |
357 | ret = PTR_ERR(wpf); | |
358 | goto done; | |
359 | } | |
360 | ||
361 | vsp1->wpf[i] = wpf; | |
362 | list_add_tail(&wpf->entity.list_dev, &vsp1->entities); | |
9d40637a | 363 | |
5aa2eb3c | 364 | if (vsp1->info->uapi) { |
f2ed459d | 365 | struct vsp1_video *video = vsp1_video_create(vsp1, wpf); |
9d40637a | 366 | |
f2ed459d LP |
367 | if (IS_ERR(video)) { |
368 | ret = PTR_ERR(video); | |
369 | goto done; | |
370 | } | |
371 | ||
372 | list_add_tail(&video->list, &vsp1->videos); | |
373 | wpf->entity.sink = &video->video.entity; | |
374 | } | |
26e0ca22 LP |
375 | } |
376 | ||
7213fe7e JMC |
377 | /* Register all subdevs. */ |
378 | list_for_each_entry(entity, &vsp1->entities, list_dev) { | |
379 | ret = v4l2_device_register_subdev(&vsp1->v4l2_dev, | |
380 | &entity->subdev); | |
381 | if (ret < 0) | |
382 | goto done; | |
383 | } | |
384 | ||
26e0ca22 | 385 | /* Create links. */ |
5aa2eb3c | 386 | if (vsp1->info->uapi) |
f3af9572 LP |
387 | ret = vsp1_uapi_create_links(vsp1); |
388 | else | |
389 | ret = vsp1_drm_create_links(vsp1); | |
a07dcc53 LP |
390 | if (ret < 0) |
391 | goto done; | |
26e0ca22 | 392 | |
f3af9572 LP |
393 | /* Register subdev nodes if the userspace API is enabled or initialize |
394 | * the DRM pipeline otherwise. | |
395 | */ | |
aa0bad33 | 396 | if (vsp1->info->uapi) { |
f2ed459d | 397 | ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev); |
aa0bad33 LP |
398 | if (ret < 0) |
399 | goto done; | |
9832e155 | 400 | |
aa0bad33 LP |
401 | ret = media_device_register(mdev); |
402 | } else { | |
403 | ret = vsp1_drm_init(vsp1); | |
404 | } | |
26e0ca22 LP |
405 | |
406 | done: | |
407 | if (ret < 0) | |
408 | vsp1_destroy_entities(vsp1); | |
409 | ||
410 | return ret; | |
411 | } | |
412 | ||
1517b039 | 413 | int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index) |
26e0ca22 | 414 | { |
1517b039 | 415 | unsigned int timeout; |
26e0ca22 LP |
416 | u32 status; |
417 | ||
26e0ca22 | 418 | status = vsp1_read(vsp1, VI6_STATUS); |
1517b039 TS |
419 | if (!(status & VI6_STATUS_SYS_ACT(index))) |
420 | return 0; | |
26e0ca22 | 421 | |
1517b039 TS |
422 | vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(index)); |
423 | for (timeout = 10; timeout > 0; --timeout) { | |
424 | status = vsp1_read(vsp1, VI6_STATUS); | |
425 | if (!(status & VI6_STATUS_SYS_ACT(index))) | |
426 | break; | |
26e0ca22 | 427 | |
1517b039 TS |
428 | usleep_range(1000, 2000); |
429 | } | |
26e0ca22 | 430 | |
1517b039 TS |
431 | if (!timeout) { |
432 | dev_err(vsp1->dev, "failed to reset wpf.%u\n", index); | |
433 | return -ETIMEDOUT; | |
434 | } | |
26e0ca22 | 435 | |
1517b039 TS |
436 | return 0; |
437 | } | |
26e0ca22 | 438 | |
1517b039 TS |
439 | static int vsp1_device_init(struct vsp1_device *vsp1) |
440 | { | |
441 | unsigned int i; | |
442 | int ret; | |
443 | ||
444 | /* Reset any channel that might be running. */ | |
5aa2eb3c | 445 | for (i = 0; i < vsp1->info->wpf_count; ++i) { |
1517b039 TS |
446 | ret = vsp1_reset_wpf(vsp1, i); |
447 | if (ret < 0) | |
448 | return ret; | |
26e0ca22 LP |
449 | } |
450 | ||
451 | vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) | | |
452 | (8 << VI6_CLK_DCSWT_CSTRW_SHIFT)); | |
453 | ||
5aa2eb3c | 454 | for (i = 0; i < vsp1->info->rpf_count; ++i) |
26e0ca22 LP |
455 | vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED); |
456 | ||
5aa2eb3c | 457 | for (i = 0; i < vsp1->info->uds_count; ++i) |
26e0ca22 LP |
458 | vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED); |
459 | ||
460 | vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED); | |
461 | vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED); | |
462 | vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED); | |
463 | vsp1_write(vsp1, VI6_DPR_HST_ROUTE, VI6_DPR_NODE_UNUSED); | |
464 | vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED); | |
465 | vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED); | |
466 | ||
467 | vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | | |
468 | (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT)); | |
469 | vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | | |
470 | (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT)); | |
471 | ||
c2dd2513 | 472 | vsp1_dlm_setup(vsp1); |
1517b039 | 473 | |
26e0ca22 LP |
474 | return 0; |
475 | } | |
476 | ||
477 | /* | |
478 | * vsp1_device_get - Acquire the VSP1 device | |
479 | * | |
1e6af546 | 480 | * Make sure the device is not suspended and initialize it if needed. |
26e0ca22 | 481 | * |
4c16d6a0 | 482 | * Return 0 on success or a negative error code otherwise. |
26e0ca22 | 483 | */ |
4c16d6a0 | 484 | int vsp1_device_get(struct vsp1_device *vsp1) |
26e0ca22 | 485 | { |
1e6af546 | 486 | int ret; |
26e0ca22 | 487 | |
1e6af546 LP |
488 | ret = pm_runtime_get_sync(vsp1->dev); |
489 | return ret < 0 ? ret : 0; | |
26e0ca22 LP |
490 | } |
491 | ||
492 | /* | |
493 | * vsp1_device_put - Release the VSP1 device | |
494 | * | |
495 | * Decrement the VSP1 reference count and cleanup the device if the last | |
496 | * reference is released. | |
497 | */ | |
498 | void vsp1_device_put(struct vsp1_device *vsp1) | |
499 | { | |
1e6af546 | 500 | pm_runtime_put_sync(vsp1->dev); |
26e0ca22 LP |
501 | } |
502 | ||
503 | /* ----------------------------------------------------------------------------- | |
504 | * Power Management | |
505 | */ | |
506 | ||
af2e14bb | 507 | static int __maybe_unused vsp1_pm_suspend(struct device *dev) |
26e0ca22 LP |
508 | { |
509 | struct vsp1_device *vsp1 = dev_get_drvdata(dev); | |
510 | ||
1e6af546 LP |
511 | vsp1_pipelines_suspend(vsp1); |
512 | pm_runtime_force_suspend(vsp1->dev); | |
26e0ca22 | 513 | |
1e6af546 LP |
514 | return 0; |
515 | } | |
26e0ca22 | 516 | |
af2e14bb | 517 | static int __maybe_unused vsp1_pm_resume(struct device *dev) |
1e6af546 LP |
518 | { |
519 | struct vsp1_device *vsp1 = dev_get_drvdata(dev); | |
139c9286 | 520 | |
1e6af546 LP |
521 | pm_runtime_force_resume(vsp1->dev); |
522 | vsp1_pipelines_resume(vsp1); | |
139c9286 | 523 | |
26e0ca22 LP |
524 | return 0; |
525 | } | |
526 | ||
af2e14bb | 527 | static int __maybe_unused vsp1_pm_runtime_suspend(struct device *dev) |
26e0ca22 | 528 | { |
94fcdf82 LP |
529 | struct vsp1_device *vsp1 = dev_get_drvdata(dev); |
530 | ||
531 | rcar_fcp_disable(vsp1->fcp); | |
532 | ||
1e6af546 LP |
533 | return 0; |
534 | } | |
26e0ca22 | 535 | |
af2e14bb | 536 | static int __maybe_unused vsp1_pm_runtime_resume(struct device *dev) |
1e6af546 LP |
537 | { |
538 | struct vsp1_device *vsp1 = dev_get_drvdata(dev); | |
539 | int ret; | |
139c9286 | 540 | |
1e6af546 LP |
541 | if (vsp1->info) { |
542 | ret = vsp1_device_init(vsp1); | |
c7b12cfd | 543 | if (ret < 0) |
1e6af546 | 544 | return ret; |
1e6af546 | 545 | } |
139c9286 | 546 | |
94fcdf82 | 547 | return rcar_fcp_enable(vsp1->fcp); |
26e0ca22 | 548 | } |
26e0ca22 LP |
549 | |
550 | static const struct dev_pm_ops vsp1_pm_ops = { | |
551 | SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume) | |
1e6af546 | 552 | SET_RUNTIME_PM_OPS(vsp1_pm_runtime_suspend, vsp1_pm_runtime_resume, NULL) |
26e0ca22 LP |
553 | }; |
554 | ||
555 | /* ----------------------------------------------------------------------------- | |
556 | * Platform Driver | |
557 | */ | |
558 | ||
5aa2eb3c LP |
559 | static const struct vsp1_device_info vsp1_device_infos[] = { |
560 | { | |
561 | .version = VI6_IP_VERSION_MODEL_VSPS_H2, | |
30276a73 | 562 | .gen = 2, |
1fd87bf2 | 563 | .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT |
894dde5c | 564 | | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, |
5aa2eb3c LP |
565 | .rpf_count = 5, |
566 | .uds_count = 3, | |
567 | .wpf_count = 4, | |
568 | .num_bru_inputs = 4, | |
569 | .uapi = true, | |
570 | }, { | |
571 | .version = VI6_IP_VERSION_MODEL_VSPR_H2, | |
30276a73 | 572 | .gen = 2, |
894dde5c | 573 | .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, |
5aa2eb3c | 574 | .rpf_count = 5, |
398e3d4f | 575 | .uds_count = 3, |
5aa2eb3c LP |
576 | .wpf_count = 4, |
577 | .num_bru_inputs = 4, | |
578 | .uapi = true, | |
579 | }, { | |
580 | .version = VI6_IP_VERSION_MODEL_VSPD_GEN2, | |
30276a73 | 581 | .gen = 2, |
5aa2eb3c LP |
582 | .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT, |
583 | .rpf_count = 4, | |
584 | .uds_count = 1, | |
398e3d4f | 585 | .wpf_count = 1, |
5aa2eb3c LP |
586 | .num_bru_inputs = 4, |
587 | .uapi = true, | |
588 | }, { | |
589 | .version = VI6_IP_VERSION_MODEL_VSPS_M2, | |
30276a73 | 590 | .gen = 2, |
1fd87bf2 | 591 | .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT |
894dde5c | 592 | | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, |
5aa2eb3c | 593 | .rpf_count = 5, |
398e3d4f | 594 | .uds_count = 1, |
5aa2eb3c LP |
595 | .wpf_count = 4, |
596 | .num_bru_inputs = 4, | |
597 | .uapi = true, | |
598 | }, { | |
599 | .version = VI6_IP_VERSION_MODEL_VSPI_GEN3, | |
30276a73 | 600 | .gen = 3, |
894dde5c LP |
601 | .features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU |
602 | | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP, | |
5aa2eb3c LP |
603 | .rpf_count = 1, |
604 | .uds_count = 1, | |
605 | .wpf_count = 1, | |
606 | .uapi = true, | |
607 | }, { | |
608 | .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3, | |
30276a73 | 609 | .gen = 3, |
894dde5c | 610 | .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP, |
5aa2eb3c LP |
611 | .rpf_count = 5, |
612 | .wpf_count = 1, | |
613 | .num_bru_inputs = 5, | |
614 | .uapi = true, | |
615 | }, { | |
616 | .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3, | |
30276a73 | 617 | .gen = 3, |
894dde5c LP |
618 | .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT |
619 | | VSP1_HAS_WPF_VFLIP, | |
5aa2eb3c LP |
620 | .rpf_count = 5, |
621 | .wpf_count = 1, | |
622 | .num_bru_inputs = 5, | |
623 | .uapi = true, | |
624 | }, { | |
625 | .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, | |
30276a73 | 626 | .gen = 3, |
894dde5c | 627 | .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP, |
5aa2eb3c LP |
628 | .rpf_count = 5, |
629 | .wpf_count = 2, | |
630 | .num_bru_inputs = 5, | |
631 | }, | |
632 | }; | |
0b82fb95 | 633 | |
26e0ca22 LP |
634 | static int vsp1_probe(struct platform_device *pdev) |
635 | { | |
636 | struct vsp1_device *vsp1; | |
94fcdf82 | 637 | struct device_node *fcp_node; |
26e0ca22 LP |
638 | struct resource *irq; |
639 | struct resource *io; | |
5aa2eb3c | 640 | unsigned int i; |
7f2d50f8 | 641 | u32 version; |
26e0ca22 LP |
642 | int ret; |
643 | ||
644 | vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL); | |
645 | if (vsp1 == NULL) | |
646 | return -ENOMEM; | |
647 | ||
648 | vsp1->dev = &pdev->dev; | |
26e0ca22 | 649 | INIT_LIST_HEAD(&vsp1->entities); |
9d40637a | 650 | INIT_LIST_HEAD(&vsp1->videos); |
26e0ca22 | 651 | |
1e6af546 LP |
652 | platform_set_drvdata(pdev, vsp1); |
653 | ||
c7b12cfd | 654 | /* I/O and IRQ resources (clock managed by the clock PM domain) */ |
26e0ca22 LP |
655 | io = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
656 | vsp1->mmio = devm_ioremap_resource(&pdev->dev, io); | |
cbec6d3a MCC |
657 | if (IS_ERR(vsp1->mmio)) |
658 | return PTR_ERR(vsp1->mmio); | |
26e0ca22 | 659 | |
26e0ca22 LP |
660 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
661 | if (!irq) { | |
662 | dev_err(&pdev->dev, "missing IRQ\n"); | |
663 | return -EINVAL; | |
664 | } | |
665 | ||
666 | ret = devm_request_irq(&pdev->dev, irq->start, vsp1_irq_handler, | |
667 | IRQF_SHARED, dev_name(&pdev->dev), vsp1); | |
668 | if (ret < 0) { | |
669 | dev_err(&pdev->dev, "failed to request IRQ\n"); | |
670 | return ret; | |
671 | } | |
672 | ||
94fcdf82 LP |
673 | /* FCP (optional) */ |
674 | fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0); | |
675 | if (fcp_node) { | |
676 | vsp1->fcp = rcar_fcp_get(fcp_node); | |
677 | of_node_put(fcp_node); | |
678 | if (IS_ERR(vsp1->fcp)) { | |
679 | dev_dbg(&pdev->dev, "FCP not found (%ld)\n", | |
680 | PTR_ERR(vsp1->fcp)); | |
681 | return PTR_ERR(vsp1->fcp); | |
682 | } | |
683 | } | |
684 | ||
7f2d50f8 | 685 | /* Configure device parameters based on the version register. */ |
1e6af546 LP |
686 | pm_runtime_enable(&pdev->dev); |
687 | ||
688 | ret = pm_runtime_get_sync(&pdev->dev); | |
7f2d50f8 | 689 | if (ret < 0) |
1e6af546 | 690 | goto done; |
7f2d50f8 LP |
691 | |
692 | version = vsp1_read(vsp1, VI6_IP_VERSION); | |
1e6af546 | 693 | pm_runtime_put_sync(&pdev->dev); |
7f2d50f8 | 694 | |
5aa2eb3c LP |
695 | for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { |
696 | if ((version & VI6_IP_VERSION_MODEL_MASK) == | |
697 | vsp1_device_infos[i].version) { | |
698 | vsp1->info = &vsp1_device_infos[i]; | |
699 | break; | |
700 | } | |
701 | } | |
7f2d50f8 | 702 | |
5aa2eb3c LP |
703 | if (!vsp1->info) { |
704 | dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version); | |
1e6af546 LP |
705 | ret = -ENXIO; |
706 | goto done; | |
7f2d50f8 LP |
707 | } |
708 | ||
5aa2eb3c LP |
709 | dev_dbg(&pdev->dev, "IP version 0x%08x\n", version); |
710 | ||
26e0ca22 LP |
711 | /* Instanciate entities */ |
712 | ret = vsp1_create_entities(vsp1); | |
713 | if (ret < 0) { | |
714 | dev_err(&pdev->dev, "failed to create entities\n"); | |
1e6af546 | 715 | goto done; |
26e0ca22 LP |
716 | } |
717 | ||
1e6af546 LP |
718 | done: |
719 | if (ret) | |
720 | pm_runtime_disable(&pdev->dev); | |
26e0ca22 | 721 | |
1e6af546 | 722 | return ret; |
26e0ca22 LP |
723 | } |
724 | ||
725 | static int vsp1_remove(struct platform_device *pdev) | |
726 | { | |
727 | struct vsp1_device *vsp1 = platform_get_drvdata(pdev); | |
728 | ||
729 | vsp1_destroy_entities(vsp1); | |
94fcdf82 | 730 | rcar_fcp_put(vsp1->fcp); |
26e0ca22 | 731 | |
1e6af546 LP |
732 | pm_runtime_disable(&pdev->dev); |
733 | ||
26e0ca22 LP |
734 | return 0; |
735 | } | |
736 | ||
0b82fb95 LP |
737 | static const struct of_device_id vsp1_of_match[] = { |
738 | { .compatible = "renesas,vsp1" }, | |
7f2d50f8 | 739 | { .compatible = "renesas,vsp2" }, |
0b82fb95 LP |
740 | { }, |
741 | }; | |
742 | ||
26e0ca22 LP |
743 | static struct platform_driver vsp1_platform_driver = { |
744 | .probe = vsp1_probe, | |
745 | .remove = vsp1_remove, | |
746 | .driver = { | |
26e0ca22 LP |
747 | .name = "vsp1", |
748 | .pm = &vsp1_pm_ops, | |
0b82fb95 | 749 | .of_match_table = vsp1_of_match, |
26e0ca22 LP |
750 | }, |
751 | }; | |
752 | ||
753 | module_platform_driver(vsp1_platform_driver); | |
754 | ||
755 | MODULE_ALIAS("vsp1"); | |
756 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | |
757 | MODULE_DESCRIPTION("Renesas VSP1 Driver"); | |
758 | MODULE_LICENSE("GPL"); |