Commit | Line | Data |
---|---|---|
dba4a180 LP |
1 | /* |
2 | * vsp1_pipe.c -- R-Car VSP1 Pipeline | |
3 | * | |
4 | * Copyright (C) 2013-2015 Renesas Electronics Corporation | |
5 | * | |
6 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | */ | |
13 | ||
14 | #include <linux/list.h> | |
15 | #include <linux/sched.h> | |
16 | #include <linux/wait.h> | |
17 | ||
18 | #include <media/media-entity.h> | |
19 | #include <media/v4l2-subdev.h> | |
20 | ||
21 | #include "vsp1.h" | |
22 | #include "vsp1_bru.h" | |
23 | #include "vsp1_entity.h" | |
24 | #include "vsp1_pipe.h" | |
25 | #include "vsp1_rwpf.h" | |
26 | #include "vsp1_uds.h" | |
27 | ||
28 | /* ----------------------------------------------------------------------------- | |
29 | * Pipeline Management | |
30 | */ | |
31 | ||
32 | void vsp1_pipeline_reset(struct vsp1_pipeline *pipe) | |
33 | { | |
34 | if (pipe->bru) { | |
35 | struct vsp1_bru *bru = to_bru(&pipe->bru->subdev); | |
36 | unsigned int i; | |
37 | ||
38 | for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i) | |
39 | bru->inputs[i].rpf = NULL; | |
40 | } | |
41 | ||
42 | INIT_LIST_HEAD(&pipe->entities); | |
43 | pipe->state = VSP1_PIPELINE_STOPPED; | |
44 | pipe->buffers_ready = 0; | |
45 | pipe->num_inputs = 0; | |
46 | pipe->output = NULL; | |
47 | pipe->bru = NULL; | |
48 | pipe->lif = NULL; | |
49 | pipe->uds = NULL; | |
50 | } | |
51 | ||
52 | void vsp1_pipeline_run(struct vsp1_pipeline *pipe) | |
53 | { | |
54 | struct vsp1_device *vsp1 = pipe->output->entity.vsp1; | |
55 | ||
56 | vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index), VI6_CMD_STRCMD); | |
57 | pipe->state = VSP1_PIPELINE_RUNNING; | |
58 | pipe->buffers_ready = 0; | |
59 | } | |
60 | ||
61 | bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe) | |
62 | { | |
63 | unsigned long flags; | |
64 | bool stopped; | |
65 | ||
66 | spin_lock_irqsave(&pipe->irqlock, flags); | |
67 | stopped = pipe->state == VSP1_PIPELINE_STOPPED; | |
68 | spin_unlock_irqrestore(&pipe->irqlock, flags); | |
69 | ||
70 | return stopped; | |
71 | } | |
72 | ||
73 | int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) | |
74 | { | |
75 | struct vsp1_entity *entity; | |
76 | unsigned long flags; | |
77 | int ret; | |
78 | ||
79 | spin_lock_irqsave(&pipe->irqlock, flags); | |
80 | if (pipe->state == VSP1_PIPELINE_RUNNING) | |
81 | pipe->state = VSP1_PIPELINE_STOPPING; | |
82 | spin_unlock_irqrestore(&pipe->irqlock, flags); | |
83 | ||
84 | ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), | |
85 | msecs_to_jiffies(500)); | |
86 | ret = ret == 0 ? -ETIMEDOUT : 0; | |
87 | ||
88 | list_for_each_entry(entity, &pipe->entities, list_pipe) { | |
89 | if (entity->route && entity->route->reg) | |
90 | vsp1_write(entity->vsp1, entity->route->reg, | |
91 | VI6_DPR_NODE_UNUSED); | |
92 | ||
93 | v4l2_subdev_call(&entity->subdev, video, s_stream, 0); | |
94 | } | |
95 | ||
96 | return ret; | |
97 | } | |
98 | ||
99 | bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) | |
100 | { | |
101 | unsigned int mask; | |
102 | ||
103 | mask = ((1 << pipe->num_inputs) - 1) << 1; | |
104 | if (!pipe->lif) | |
105 | mask |= 1 << 0; | |
106 | ||
107 | return pipe->buffers_ready == mask; | |
108 | } | |
109 | ||
110 | void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) | |
111 | { | |
112 | enum vsp1_pipeline_state state; | |
113 | unsigned long flags; | |
114 | ||
115 | if (pipe == NULL) | |
116 | return; | |
117 | ||
118 | /* Signal frame end to the pipeline handler. */ | |
119 | pipe->frame_end(pipe); | |
120 | ||
121 | spin_lock_irqsave(&pipe->irqlock, flags); | |
122 | ||
123 | state = pipe->state; | |
124 | pipe->state = VSP1_PIPELINE_STOPPED; | |
125 | ||
126 | /* If a stop has been requested, mark the pipeline as stopped and | |
127 | * return. | |
128 | */ | |
129 | if (state == VSP1_PIPELINE_STOPPING) { | |
130 | wake_up(&pipe->wq); | |
131 | goto done; | |
132 | } | |
133 | ||
134 | /* Restart the pipeline if ready. */ | |
135 | if (vsp1_pipeline_ready(pipe)) | |
136 | vsp1_pipeline_run(pipe); | |
137 | ||
138 | done: | |
139 | spin_unlock_irqrestore(&pipe->irqlock, flags); | |
140 | } | |
141 | ||
142 | /* | |
143 | * Propagate the alpha value through the pipeline. | |
144 | * | |
145 | * As the UDS has restricted scaling capabilities when the alpha component needs | |
146 | * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha | |
147 | * value. The UDS then outputs a fixed alpha value which needs to be programmed | |
148 | * from the input RPF alpha. | |
149 | */ | |
150 | void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, | |
151 | struct vsp1_entity *input, | |
152 | unsigned int alpha) | |
153 | { | |
154 | struct vsp1_entity *entity; | |
155 | struct media_pad *pad; | |
156 | ||
157 | pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]); | |
158 | ||
159 | while (pad) { | |
160 | if (!is_media_entity_v4l2_subdev(pad->entity)) | |
161 | break; | |
162 | ||
163 | entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); | |
164 | ||
165 | /* The BRU background color has a fixed alpha value set to 255, | |
166 | * the output alpha value is thus always equal to 255. | |
167 | */ | |
168 | if (entity->type == VSP1_ENTITY_BRU) | |
169 | alpha = 255; | |
170 | ||
171 | if (entity->type == VSP1_ENTITY_UDS) { | |
172 | struct vsp1_uds *uds = to_uds(&entity->subdev); | |
173 | ||
174 | vsp1_uds_set_alpha(uds, alpha); | |
175 | break; | |
176 | } | |
177 | ||
178 | pad = &entity->pads[entity->source_pad]; | |
179 | pad = media_entity_remote_pad(pad); | |
180 | } | |
181 | } | |
182 | ||
183 | void vsp1_pipelines_suspend(struct vsp1_device *vsp1) | |
184 | { | |
185 | unsigned long flags; | |
186 | unsigned int i; | |
187 | int ret; | |
188 | ||
189 | /* To avoid increasing the system suspend time needlessly, loop over the | |
190 | * pipelines twice, first to set them all to the stopping state, and | |
191 | * then to wait for the stop to complete. | |
192 | */ | |
193 | for (i = 0; i < vsp1->pdata.wpf_count; ++i) { | |
194 | struct vsp1_rwpf *wpf = vsp1->wpf[i]; | |
195 | struct vsp1_pipeline *pipe; | |
196 | ||
197 | if (wpf == NULL) | |
198 | continue; | |
199 | ||
200 | pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); | |
201 | if (pipe == NULL) | |
202 | continue; | |
203 | ||
204 | spin_lock_irqsave(&pipe->irqlock, flags); | |
205 | if (pipe->state == VSP1_PIPELINE_RUNNING) | |
206 | pipe->state = VSP1_PIPELINE_STOPPING; | |
207 | spin_unlock_irqrestore(&pipe->irqlock, flags); | |
208 | } | |
209 | ||
210 | for (i = 0; i < vsp1->pdata.wpf_count; ++i) { | |
211 | struct vsp1_rwpf *wpf = vsp1->wpf[i]; | |
212 | struct vsp1_pipeline *pipe; | |
213 | ||
214 | if (wpf == NULL) | |
215 | continue; | |
216 | ||
217 | pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); | |
218 | if (pipe == NULL) | |
219 | continue; | |
220 | ||
221 | ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), | |
222 | msecs_to_jiffies(500)); | |
223 | if (ret == 0) | |
224 | dev_warn(vsp1->dev, "pipeline %u stop timeout\n", | |
225 | wpf->entity.index); | |
226 | } | |
227 | } | |
228 | ||
229 | void vsp1_pipelines_resume(struct vsp1_device *vsp1) | |
230 | { | |
231 | unsigned int i; | |
232 | ||
233 | /* Resume pipeline all running pipelines. */ | |
234 | for (i = 0; i < vsp1->pdata.wpf_count; ++i) { | |
235 | struct vsp1_rwpf *wpf = vsp1->wpf[i]; | |
236 | struct vsp1_pipeline *pipe; | |
237 | ||
238 | if (wpf == NULL) | |
239 | continue; | |
240 | ||
241 | pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); | |
242 | if (pipe == NULL) | |
243 | continue; | |
244 | ||
245 | if (vsp1_pipeline_ready(pipe)) | |
246 | vsp1_pipeline_run(pipe); | |
247 | } | |
248 | } |