2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifdef CONFIG_MSM_BUS_SCALING
27 #include <mach/board.h>
28 #include <mach/kgsl.h>
29 static void bs_init(struct msm_gpu
*gpu
, struct platform_device
*pdev
)
31 struct drm_device
*dev
= gpu
->dev
;
32 struct kgsl_device_platform_data
*pdata
;
35 dev_err(dev
->dev
, "could not find dtv pdata\n");
39 pdata
= pdev
->dev
.platform_data
;
40 if (pdata
->bus_scale_table
) {
41 gpu
->bsc
= msm_bus_scale_register_client(pdata
->bus_scale_table
);
42 DBG("bus scale client: %08x", gpu
->bsc
);
46 static void bs_fini(struct msm_gpu
*gpu
)
49 msm_bus_scale_unregister_client(gpu
->bsc
);
54 static void bs_set(struct msm_gpu
*gpu
, int idx
)
57 DBG("set bus scaling: %d", idx
);
58 msm_bus_scale_client_update_request(gpu
->bsc
, idx
);
62 static void bs_init(struct msm_gpu
*gpu
, struct platform_device
*pdev
) {}
63 static void bs_fini(struct msm_gpu
*gpu
) {}
64 static void bs_set(struct msm_gpu
*gpu
, int idx
) {}
67 static int enable_pwrrail(struct msm_gpu
*gpu
)
69 struct drm_device
*dev
= gpu
->dev
;
73 ret
= regulator_enable(gpu
->gpu_reg
);
75 dev_err(dev
->dev
, "failed to enable 'gpu_reg': %d\n", ret
);
81 ret
= regulator_enable(gpu
->gpu_cx
);
83 dev_err(dev
->dev
, "failed to enable 'gpu_cx': %d\n", ret
);
91 static int disable_pwrrail(struct msm_gpu
*gpu
)
94 regulator_disable(gpu
->gpu_cx
);
96 regulator_disable(gpu
->gpu_reg
);
100 static int enable_clk(struct msm_gpu
*gpu
)
102 struct clk
*rate_clk
= NULL
;
105 /* NOTE: kgsl_pwrctrl_clk() ignores grp_clks[0].. */
106 for (i
= ARRAY_SIZE(gpu
->grp_clks
) - 1; i
> 0; i
--) {
107 if (gpu
->grp_clks
[i
]) {
108 clk_prepare(gpu
->grp_clks
[i
]);
109 rate_clk
= gpu
->grp_clks
[i
];
113 if (rate_clk
&& gpu
->fast_rate
)
114 clk_set_rate(rate_clk
, gpu
->fast_rate
);
116 for (i
= ARRAY_SIZE(gpu
->grp_clks
) - 1; i
> 0; i
--)
117 if (gpu
->grp_clks
[i
])
118 clk_enable(gpu
->grp_clks
[i
]);
123 static int disable_clk(struct msm_gpu
*gpu
)
125 struct clk
*rate_clk
= NULL
;
128 /* NOTE: kgsl_pwrctrl_clk() ignores grp_clks[0].. */
129 for (i
= ARRAY_SIZE(gpu
->grp_clks
) - 1; i
> 0; i
--) {
130 if (gpu
->grp_clks
[i
]) {
131 clk_disable(gpu
->grp_clks
[i
]);
132 rate_clk
= gpu
->grp_clks
[i
];
136 if (rate_clk
&& gpu
->slow_rate
)
137 clk_set_rate(rate_clk
, gpu
->slow_rate
);
139 for (i
= ARRAY_SIZE(gpu
->grp_clks
) - 1; i
> 0; i
--)
140 if (gpu
->grp_clks
[i
])
141 clk_unprepare(gpu
->grp_clks
[i
]);
146 static int enable_axi(struct msm_gpu
*gpu
)
149 clk_prepare_enable(gpu
->ebi1_clk
);
151 bs_set(gpu
, gpu
->bus_freq
);
155 static int disable_axi(struct msm_gpu
*gpu
)
158 clk_disable_unprepare(gpu
->ebi1_clk
);
164 int msm_gpu_pm_resume(struct msm_gpu
*gpu
)
168 DBG("%s", gpu
->name
);
170 ret
= enable_pwrrail(gpu
);
174 ret
= enable_clk(gpu
);
178 ret
= enable_axi(gpu
);
185 int msm_gpu_pm_suspend(struct msm_gpu
*gpu
)
189 DBG("%s", gpu
->name
);
191 ret
= disable_axi(gpu
);
195 ret
= disable_clk(gpu
);
199 ret
= disable_pwrrail(gpu
);
207 * Hangcheck detection for locked gpu:
210 static void recover_worker(struct work_struct
*work
)
212 struct msm_gpu
*gpu
= container_of(work
, struct msm_gpu
, recover_work
);
213 struct drm_device
*dev
= gpu
->dev
;
215 dev_err(dev
->dev
, "%s: hangcheck recover!\n", gpu
->name
);
217 mutex_lock(&dev
->struct_mutex
);
218 gpu
->funcs
->recover(gpu
);
219 mutex_unlock(&dev
->struct_mutex
);
224 static void hangcheck_timer_reset(struct msm_gpu
*gpu
)
226 DBG("%s", gpu
->name
);
227 mod_timer(&gpu
->hangcheck_timer
,
228 round_jiffies_up(jiffies
+ DRM_MSM_HANGCHECK_JIFFIES
));
231 static void hangcheck_handler(unsigned long data
)
233 struct msm_gpu
*gpu
= (struct msm_gpu
*)data
;
234 struct drm_device
*dev
= gpu
->dev
;
235 struct msm_drm_private
*priv
= dev
->dev_private
;
236 uint32_t fence
= gpu
->funcs
->last_fence(gpu
);
238 if (fence
!= gpu
->hangcheck_fence
) {
239 /* some progress has been made.. ya! */
240 gpu
->hangcheck_fence
= fence
;
241 } else if (fence
< gpu
->submitted_fence
) {
242 /* no progress and not done.. hung! */
243 gpu
->hangcheck_fence
= fence
;
244 dev_err(dev
->dev
, "%s: hangcheck detected gpu lockup!\n",
246 dev_err(dev
->dev
, "%s: completed fence: %u\n",
248 dev_err(dev
->dev
, "%s: submitted fence: %u\n",
249 gpu
->name
, gpu
->submitted_fence
);
250 queue_work(priv
->wq
, &gpu
->recover_work
);
253 /* if still more pending work, reset the hangcheck timer: */
254 if (gpu
->submitted_fence
> gpu
->hangcheck_fence
)
255 hangcheck_timer_reset(gpu
);
257 /* workaround for missing irq: */
258 queue_work(priv
->wq
, &gpu
->retire_work
);
262 * Cmdstream submission/retirement:
265 static void retire_worker(struct work_struct
*work
)
267 struct msm_gpu
*gpu
= container_of(work
, struct msm_gpu
, retire_work
);
268 struct drm_device
*dev
= gpu
->dev
;
269 uint32_t fence
= gpu
->funcs
->last_fence(gpu
);
271 mutex_lock(&dev
->struct_mutex
);
273 while (!list_empty(&gpu
->active_list
)) {
274 struct msm_gem_object
*obj
;
276 obj
= list_first_entry(&gpu
->active_list
,
277 struct msm_gem_object
, mm_list
);
279 if ((obj
->read_fence
<= fence
) &&
280 (obj
->write_fence
<= fence
)) {
281 /* move to inactive: */
282 msm_gem_move_to_inactive(&obj
->base
);
283 msm_gem_put_iova(&obj
->base
, gpu
->id
);
284 drm_gem_object_unreference(&obj
->base
);
290 msm_update_fence(gpu
->dev
, fence
);
292 mutex_unlock(&dev
->struct_mutex
);
295 /* call from irq handler to schedule work to retire bo's */
296 void msm_gpu_retire(struct msm_gpu
*gpu
)
298 struct msm_drm_private
*priv
= gpu
->dev
->dev_private
;
299 queue_work(priv
->wq
, &gpu
->retire_work
);
302 /* add bo's to gpu's ring, and kick gpu: */
303 int msm_gpu_submit(struct msm_gpu
*gpu
, struct msm_gem_submit
*submit
,
304 struct msm_file_private
*ctx
)
306 struct drm_device
*dev
= gpu
->dev
;
307 struct msm_drm_private
*priv
= dev
->dev_private
;
310 mutex_lock(&dev
->struct_mutex
);
312 submit
->fence
= ++priv
->next_fence
;
314 gpu
->submitted_fence
= submit
->fence
;
316 ret
= gpu
->funcs
->submit(gpu
, submit
, ctx
);
319 for (i
= 0; i
< submit
->nr_bos
; i
++) {
320 struct msm_gem_object
*msm_obj
= submit
->bos
[i
].obj
;
322 /* can't happen yet.. but when we add 2d support we'll have
323 * to deal w/ cross-ring synchronization:
325 WARN_ON(is_active(msm_obj
) && (msm_obj
->gpu
!= gpu
));
327 if (!is_active(msm_obj
)) {
330 /* ring takes a reference to the bo and iova: */
331 drm_gem_object_reference(&msm_obj
->base
);
332 msm_gem_get_iova_locked(&msm_obj
->base
,
333 submit
->gpu
->id
, &iova
);
336 if (submit
->bos
[i
].flags
& MSM_SUBMIT_BO_READ
)
337 msm_gem_move_to_active(&msm_obj
->base
, gpu
, false, submit
->fence
);
339 if (submit
->bos
[i
].flags
& MSM_SUBMIT_BO_WRITE
)
340 msm_gem_move_to_active(&msm_obj
->base
, gpu
, true, submit
->fence
);
342 hangcheck_timer_reset(gpu
);
343 mutex_unlock(&dev
->struct_mutex
);
352 static irqreturn_t
irq_handler(int irq
, void *data
)
354 struct msm_gpu
*gpu
= data
;
355 return gpu
->funcs
->irq(gpu
);
358 static const char *clk_names
[] = {
359 "src_clk", "core_clk", "iface_clk", "mem_clk", "mem_iface_clk",
362 int msm_gpu_init(struct drm_device
*drm
, struct platform_device
*pdev
,
363 struct msm_gpu
*gpu
, const struct msm_gpu_funcs
*funcs
,
364 const char *name
, const char *ioname
, const char *irqname
, int ringsz
)
372 INIT_LIST_HEAD(&gpu
->active_list
);
373 INIT_WORK(&gpu
->retire_work
, retire_worker
);
374 INIT_WORK(&gpu
->recover_work
, recover_worker
);
376 setup_timer(&gpu
->hangcheck_timer
, hangcheck_handler
,
379 BUG_ON(ARRAY_SIZE(clk_names
) != ARRAY_SIZE(gpu
->grp_clks
));
382 gpu
->mmio
= msm_ioremap(pdev
, ioname
, name
);
383 if (IS_ERR(gpu
->mmio
)) {
384 ret
= PTR_ERR(gpu
->mmio
);
389 gpu
->irq
= platform_get_irq_byname(pdev
, irqname
);
392 dev_err(drm
->dev
, "failed to get irq: %d\n", ret
);
396 ret
= devm_request_irq(&pdev
->dev
, gpu
->irq
, irq_handler
,
397 IRQF_TRIGGER_HIGH
, gpu
->name
, gpu
);
399 dev_err(drm
->dev
, "failed to request IRQ%u: %d\n", gpu
->irq
, ret
);
403 /* Acquire clocks: */
404 for (i
= 0; i
< ARRAY_SIZE(clk_names
); i
++) {
405 gpu
->grp_clks
[i
] = devm_clk_get(&pdev
->dev
, clk_names
[i
]);
406 DBG("grp_clks[%s]: %p", clk_names
[i
], gpu
->grp_clks
[i
]);
407 if (IS_ERR(gpu
->grp_clks
[i
]))
408 gpu
->grp_clks
[i
] = NULL
;
411 gpu
->ebi1_clk
= devm_clk_get(&pdev
->dev
, "bus_clk");
412 DBG("ebi1_clk: %p", gpu
->ebi1_clk
);
413 if (IS_ERR(gpu
->ebi1_clk
))
414 gpu
->ebi1_clk
= NULL
;
416 /* Acquire regulators: */
417 gpu
->gpu_reg
= devm_regulator_get(&pdev
->dev
, "vdd");
418 DBG("gpu_reg: %p", gpu
->gpu_reg
);
419 if (IS_ERR(gpu
->gpu_reg
))
422 gpu
->gpu_cx
= devm_regulator_get(&pdev
->dev
, "vddcx");
423 DBG("gpu_cx: %p", gpu
->gpu_cx
);
424 if (IS_ERR(gpu
->gpu_cx
))
427 /* Setup IOMMU.. eventually we will (I think) do this once per context
428 * and have separate page tables per context. For now, to keep things
429 * simple and to get something working, just use a single address space:
431 gpu
->iommu
= iommu_domain_alloc(&platform_bus_type
);
433 dev_err(drm
->dev
, "failed to allocate IOMMU\n");
437 gpu
->id
= msm_register_iommu(drm
, gpu
->iommu
);
439 /* Create ringbuffer: */
440 gpu
->rb
= msm_ringbuffer_new(gpu
, ringsz
);
441 if (IS_ERR(gpu
->rb
)) {
442 ret
= PTR_ERR(gpu
->rb
);
444 dev_err(drm
->dev
, "could not create ringbuffer: %d\n", ret
);
448 ret
= msm_gem_get_iova_locked(gpu
->rb
->bo
, gpu
->id
, &gpu
->rb_iova
);
451 dev_err(drm
->dev
, "could not map ringbuffer: %d\n", ret
);
463 void msm_gpu_cleanup(struct msm_gpu
*gpu
)
465 DBG("%s", gpu
->name
);
467 WARN_ON(!list_empty(&gpu
->active_list
));
473 msm_gem_put_iova(gpu
->rb
->bo
, gpu
->id
);
474 msm_ringbuffer_destroy(gpu
->rb
);
478 iommu_domain_free(gpu
->iommu
);