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