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