msm_gpu.c 19.4 KB
Newer Older
R
Rob Clark 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * Copyright (C) 2013 Red Hat
 * Author: Rob Clark <robdclark@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "msm_gpu.h"
#include "msm_gem.h"
20
#include "msm_mmu.h"
21
#include "msm_fence.h"
R
Rob Clark 已提交
22

23 24
#include <linux/string_helpers.h>

R
Rob Clark 已提交
25 26 27 28 29

/*
 * Power Management:
 */

30
#ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING
R
Rob Clark 已提交
31
#include <mach/board.h>
R
Rob Clark 已提交
32
static void bs_init(struct msm_gpu *gpu)
R
Rob Clark 已提交
33
{
R
Rob Clark 已提交
34 35
	if (gpu->bus_scale_table) {
		gpu->bsc = msm_bus_scale_register_client(gpu->bus_scale_table);
R
Rob Clark 已提交
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
		DBG("bus scale client: %08x", gpu->bsc);
	}
}

static void bs_fini(struct msm_gpu *gpu)
{
	if (gpu->bsc) {
		msm_bus_scale_unregister_client(gpu->bsc);
		gpu->bsc = 0;
	}
}

static void bs_set(struct msm_gpu *gpu, int idx)
{
	if (gpu->bsc) {
		DBG("set bus scaling: %d", idx);
		msm_bus_scale_client_update_request(gpu->bsc, idx);
	}
}
#else
R
Rob Clark 已提交
56
static void bs_init(struct msm_gpu *gpu) {}
R
Rob Clark 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
static void bs_fini(struct msm_gpu *gpu) {}
static void bs_set(struct msm_gpu *gpu, int idx) {}
#endif

static int enable_pwrrail(struct msm_gpu *gpu)
{
	struct drm_device *dev = gpu->dev;
	int ret = 0;

	if (gpu->gpu_reg) {
		ret = regulator_enable(gpu->gpu_reg);
		if (ret) {
			dev_err(dev->dev, "failed to enable 'gpu_reg': %d\n", ret);
			return ret;
		}
	}

	if (gpu->gpu_cx) {
		ret = regulator_enable(gpu->gpu_cx);
		if (ret) {
			dev_err(dev->dev, "failed to enable 'gpu_cx': %d\n", ret);
			return ret;
		}
	}

	return 0;
}

static int disable_pwrrail(struct msm_gpu *gpu)
{
	if (gpu->gpu_cx)
		regulator_disable(gpu->gpu_cx);
	if (gpu->gpu_reg)
		regulator_disable(gpu->gpu_reg);
	return 0;
}

static int enable_clk(struct msm_gpu *gpu)
{
	int i;

98 99
	if (gpu->core_clk && gpu->fast_rate)
		clk_set_rate(gpu->core_clk, gpu->fast_rate);
R
Rob Clark 已提交
100

101
	/* Set the RBBM timer rate to 19.2Mhz */
102 103
	if (gpu->rbbmtimer_clk)
		clk_set_rate(gpu->rbbmtimer_clk, 19200000);
104

105
	for (i = gpu->nr_clocks - 1; i >= 0; i--)
106 107
		if (gpu->grp_clks[i])
			clk_prepare(gpu->grp_clks[i]);
R
Rob Clark 已提交
108

109
	for (i = gpu->nr_clocks - 1; i >= 0; i--)
R
Rob Clark 已提交
110 111 112 113 114 115 116 117 118 119
		if (gpu->grp_clks[i])
			clk_enable(gpu->grp_clks[i]);

	return 0;
}

static int disable_clk(struct msm_gpu *gpu)
{
	int i;

120
	for (i = gpu->nr_clocks - 1; i >= 0; i--)
121
		if (gpu->grp_clks[i])
R
Rob Clark 已提交
122 123
			clk_disable(gpu->grp_clks[i]);

124
	for (i = gpu->nr_clocks - 1; i >= 0; i--)
R
Rob Clark 已提交
125 126 127
		if (gpu->grp_clks[i])
			clk_unprepare(gpu->grp_clks[i]);

128 129 130 131 132
	/*
	 * Set the clock to a deliberately low rate. On older targets the clock
	 * speed had to be non zero to avoid problems. On newer targets this
	 * will be rounded down to zero anyway so it all works out.
	 */
133 134
	if (gpu->core_clk)
		clk_set_rate(gpu->core_clk, 27000000);
135

136 137
	if (gpu->rbbmtimer_clk)
		clk_set_rate(gpu->rbbmtimer_clk, 0);
138

R
Rob Clark 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
	return 0;
}

static int enable_axi(struct msm_gpu *gpu)
{
	if (gpu->ebi1_clk)
		clk_prepare_enable(gpu->ebi1_clk);
	if (gpu->bus_freq)
		bs_set(gpu, gpu->bus_freq);
	return 0;
}

static int disable_axi(struct msm_gpu *gpu)
{
	if (gpu->ebi1_clk)
		clk_disable_unprepare(gpu->ebi1_clk);
	if (gpu->bus_freq)
		bs_set(gpu, 0);
	return 0;
}

int msm_gpu_pm_resume(struct msm_gpu *gpu)
{
	int ret;

R
Rob Clark 已提交
164
	DBG("%s", gpu->name);
R
Rob Clark 已提交
165 166 167 168 169 170 171 172 173 174 175 176 177

	ret = enable_pwrrail(gpu);
	if (ret)
		return ret;

	ret = enable_clk(gpu);
	if (ret)
		return ret;

	ret = enable_axi(gpu);
	if (ret)
		return ret;

R
Rob Clark 已提交
178 179
	gpu->needs_hw_init = true;

R
Rob Clark 已提交
180 181 182 183 184 185 186
	return 0;
}

int msm_gpu_pm_suspend(struct msm_gpu *gpu)
{
	int ret;

R
Rob Clark 已提交
187
	DBG("%s", gpu->name);
R
Rob Clark 已提交
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

	ret = disable_axi(gpu);
	if (ret)
		return ret;

	ret = disable_clk(gpu);
	if (ret)
		return ret;

	ret = disable_pwrrail(gpu);
	if (ret)
		return ret;

	return 0;
}

R
Rob Clark 已提交
204
int msm_gpu_hw_init(struct msm_gpu *gpu)
205
{
R
Rob Clark 已提交
206
	int ret;
207

208 209
	WARN_ON(!mutex_is_locked(&gpu->dev->struct_mutex));

R
Rob Clark 已提交
210 211
	if (!gpu->needs_hw_init)
		return 0;
212

R
Rob Clark 已提交
213 214 215 216 217
	disable_irq(gpu->irq);
	ret = gpu->funcs->hw_init(gpu);
	if (!ret)
		gpu->needs_hw_init = false;
	enable_irq(gpu->irq);
218

R
Rob Clark 已提交
219
	return ret;
220 221
}

222 223 224 225
/*
 * Hangcheck detection for locked gpu:
 */

226 227 228 229 230 231 232 233 234 235 236 237 238 239
static void update_fences(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
		uint32_t fence)
{
	struct msm_gem_submit *submit;

	list_for_each_entry(submit, &ring->submits, node) {
		if (submit->seqno > fence)
			break;

		msm_update_fence(submit->ring->fctx,
			submit->fence->seqno);
	}
}

240 241 242 243 244 245 246 247 248 249 250 251 252 253
static struct msm_gem_submit *
find_submit(struct msm_ringbuffer *ring, uint32_t fence)
{
	struct msm_gem_submit *submit;

	WARN_ON(!mutex_is_locked(&ring->gpu->dev->struct_mutex));

	list_for_each_entry(submit, &ring->submits, node)
		if (submit->seqno == fence)
			return submit;

	return NULL;
}

R
Rob Clark 已提交
254
static void retire_submits(struct msm_gpu *gpu);
255

256 257 258 259
static void recover_worker(struct work_struct *work)
{
	struct msm_gpu *gpu = container_of(work, struct msm_gpu, recover_work);
	struct drm_device *dev = gpu->dev;
260
	struct msm_drm_private *priv = dev->dev_private;
261
	struct msm_gem_submit *submit;
262 263 264
	struct msm_ringbuffer *cur_ring = gpu->funcs->active_ring(gpu);
	int i;

265
	mutex_lock(&dev->struct_mutex);
266

267
	dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name);
268

269
	submit = find_submit(cur_ring, cur_ring->memptrs->fence + 1);
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
	if (submit) {
		struct task_struct *task;

		rcu_read_lock();
		task = pid_task(submit->pid, PIDTYPE_PID);
		if (task) {
			char *cmd;

			/*
			 * So slightly annoying, in other paths like
			 * mmap'ing gem buffers, mmap_sem is acquired
			 * before struct_mutex, which means we can't
			 * hold struct_mutex across the call to
			 * get_cmdline().  But submits are retired
			 * from the same in-order workqueue, so we can
			 * safely drop the lock here without worrying
			 * about the submit going away.
			 */
			mutex_unlock(&dev->struct_mutex);
			cmd = kstrdup_quotable_cmdline(task, GFP_KERNEL);
			mutex_lock(&dev->struct_mutex);

			dev_err(dev->dev, "%s: offending task: %s (%s)\n",
				gpu->name, task->comm, cmd);
294 295 296 297 298

			msm_rd_dump_submit(priv->hangrd, submit,
				"offending task: %s (%s)", task->comm, cmd);
		} else {
			msm_rd_dump_submit(priv->hangrd, submit, NULL);
299
		}
300
		rcu_read_unlock();
301 302 303 304 305 306 307 308 309 310 311 312
	}


	/*
	 * Update all the rings with the latest and greatest fence.. this
	 * needs to happen after msm_rd_dump_submit() to ensure that the
	 * bo's referenced by the offending submit are still around.
	 */
	for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) {
		struct msm_ringbuffer *ring = gpu->rb[i];

		uint32_t fence = ring->memptrs->fence;
313

314 315 316 317 318 319 320 321
		/*
		 * For the current (faulting?) ring/submit advance the fence by
		 * one more to clear the faulting submit
		 */
		if (ring == cur_ring)
			fence++;

		update_fences(gpu, ring, fence);
322 323 324
	}

	if (msm_gpu_active(gpu)) {
325
		/* retire completed submits, plus the one that hung: */
R
Rob Clark 已提交
326
		retire_submits(gpu);
327

R
Rob Clark 已提交
328
		pm_runtime_get_sync(&gpu->pdev->dev);
329
		gpu->funcs->recover(gpu);
R
Rob Clark 已提交
330
		pm_runtime_put_sync(&gpu->pdev->dev);
331

332 333 334 335
		/*
		 * Replay all remaining submits starting with highest priority
		 * ring
		 */
336
		for (i = 0; i < gpu->nr_rings; i++) {
337 338 339 340
			struct msm_ringbuffer *ring = gpu->rb[i];

			list_for_each_entry(submit, &ring->submits, node)
				gpu->funcs->submit(gpu, submit, NULL);
341
		}
342
	}
343

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
	mutex_unlock(&dev->struct_mutex);

	msm_gpu_retire(gpu);
}

static void hangcheck_timer_reset(struct msm_gpu *gpu)
{
	DBG("%s", gpu->name);
	mod_timer(&gpu->hangcheck_timer,
			round_jiffies_up(jiffies + DRM_MSM_HANGCHECK_JIFFIES));
}

static void hangcheck_handler(unsigned long data)
{
	struct msm_gpu *gpu = (struct msm_gpu *)data;
R
Rob Clark 已提交
359 360
	struct drm_device *dev = gpu->dev;
	struct msm_drm_private *priv = dev->dev_private;
361 362
	struct msm_ringbuffer *ring = gpu->funcs->active_ring(gpu);
	uint32_t fence = ring->memptrs->fence;
363

364
	if (fence != ring->hangcheck_fence) {
365
		/* some progress has been made.. ya! */
366 367
		ring->hangcheck_fence = fence;
	} else if (fence < ring->seqno) {
368
		/* no progress and not done.. hung! */
369 370 371
		ring->hangcheck_fence = fence;
		dev_err(dev->dev, "%s: hangcheck detected gpu lockup rb %d!\n",
				gpu->name, ring->id);
R
Rob Clark 已提交
372 373 374
		dev_err(dev->dev, "%s:     completed fence: %u\n",
				gpu->name, fence);
		dev_err(dev->dev, "%s:     submitted fence: %u\n",
375 376
				gpu->name, ring->seqno);

377 378 379 380
		queue_work(priv->wq, &gpu->recover_work);
	}

	/* if still more pending work, reset the hangcheck timer: */
381
	if (ring->seqno > ring->hangcheck_fence)
382
		hangcheck_timer_reset(gpu);
R
Rob Clark 已提交
383 384 385

	/* workaround for missing irq: */
	queue_work(priv->wq, &gpu->retire_work);
386 387
}

R
Rob Clark 已提交
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
/*
 * Performance Counters:
 */

/* called under perf_lock */
static int update_hw_cntrs(struct msm_gpu *gpu, uint32_t ncntrs, uint32_t *cntrs)
{
	uint32_t current_cntrs[ARRAY_SIZE(gpu->last_cntrs)];
	int i, n = min(ncntrs, gpu->num_perfcntrs);

	/* read current values: */
	for (i = 0; i < gpu->num_perfcntrs; i++)
		current_cntrs[i] = gpu_read(gpu, gpu->perfcntrs[i].sample_reg);

	/* update cntrs: */
	for (i = 0; i < n; i++)
		cntrs[i] = current_cntrs[i] - gpu->last_cntrs[i];

	/* save current values: */
	for (i = 0; i < gpu->num_perfcntrs; i++)
		gpu->last_cntrs[i] = current_cntrs[i];

	return n;
}

static void update_sw_cntrs(struct msm_gpu *gpu)
{
	ktime_t time;
	uint32_t elapsed;
	unsigned long flags;

	spin_lock_irqsave(&gpu->perf_lock, flags);
	if (!gpu->perfcntr_active)
		goto out;

	time = ktime_get();
	elapsed = ktime_to_us(ktime_sub(time, gpu->last_sample.time));

	gpu->totaltime += elapsed;
	if (gpu->last_sample.active)
		gpu->activetime += elapsed;

	gpu->last_sample.active = msm_gpu_active(gpu);
	gpu->last_sample.time = time;

out:
	spin_unlock_irqrestore(&gpu->perf_lock, flags);
}

void msm_gpu_perfcntr_start(struct msm_gpu *gpu)
{
	unsigned long flags;

R
Rob Clark 已提交
441 442
	pm_runtime_get_sync(&gpu->pdev->dev);

R
Rob Clark 已提交
443 444 445 446 447 448 449 450 451 452 453 454 455
	spin_lock_irqsave(&gpu->perf_lock, flags);
	/* we could dynamically enable/disable perfcntr registers too.. */
	gpu->last_sample.active = msm_gpu_active(gpu);
	gpu->last_sample.time = ktime_get();
	gpu->activetime = gpu->totaltime = 0;
	gpu->perfcntr_active = true;
	update_hw_cntrs(gpu, 0, NULL);
	spin_unlock_irqrestore(&gpu->perf_lock, flags);
}

void msm_gpu_perfcntr_stop(struct msm_gpu *gpu)
{
	gpu->perfcntr_active = false;
R
Rob Clark 已提交
456
	pm_runtime_put_sync(&gpu->pdev->dev);
R
Rob Clark 已提交
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
}

/* returns -errno or # of cntrs sampled */
int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
		uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs)
{
	unsigned long flags;
	int ret;

	spin_lock_irqsave(&gpu->perf_lock, flags);

	if (!gpu->perfcntr_active) {
		ret = -EINVAL;
		goto out;
	}

	*activetime = gpu->activetime;
	*totaltime = gpu->totaltime;

	gpu->activetime = gpu->totaltime = 0;

	ret = update_hw_cntrs(gpu, ncntrs, cntrs);

out:
	spin_unlock_irqrestore(&gpu->perf_lock, flags);

	return ret;
}

R
Rob Clark 已提交
486 487 488 489
/*
 * Cmdstream submission/retirement:
 */

490 491 492 493 494 495 496 497
static void retire_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
{
	int i;

	for (i = 0; i < submit->nr_bos; i++) {
		struct msm_gem_object *msm_obj = submit->bos[i].obj;
		/* move to inactive: */
		msm_gem_move_to_inactive(&msm_obj->base);
498
		msm_gem_put_iova(&msm_obj->base, gpu->aspace);
499 500 501
		drm_gem_object_unreference(&msm_obj->base);
	}

R
Rob Clark 已提交
502 503
	pm_runtime_mark_last_busy(&gpu->pdev->dev);
	pm_runtime_put_autosuspend(&gpu->pdev->dev);
504
	msm_gem_submit_free(submit);
505 506
}

R
Rob Clark 已提交
507
static void retire_submits(struct msm_gpu *gpu)
508 509
{
	struct drm_device *dev = gpu->dev;
510 511
	struct msm_gem_submit *submit, *tmp;
	int i;
512 513 514

	WARN_ON(!mutex_is_locked(&dev->struct_mutex));

515
	/* Retire the commits starting with highest priority */
516
	for (i = 0; i < gpu->nr_rings; i++) {
517
		struct msm_ringbuffer *ring = gpu->rb[i];
518

519 520 521
		list_for_each_entry_safe(submit, tmp, &ring->submits, node) {
			if (dma_fence_is_signaled(submit->fence))
				retire_submit(gpu, submit);
522 523 524 525
		}
	}
}

R
Rob Clark 已提交
526 527 528 529
static void retire_worker(struct work_struct *work)
{
	struct msm_gpu *gpu = container_of(work, struct msm_gpu, retire_work);
	struct drm_device *dev = gpu->dev;
530
	int i;
R
Rob Clark 已提交
531

532 533
	for (i = 0; i < gpu->nr_rings; i++)
		update_fences(gpu, gpu->rb[i], gpu->rb[i]->memptrs->fence);
R
Rob Clark 已提交
534

R
Rob Clark 已提交
535
	mutex_lock(&dev->struct_mutex);
R
Rob Clark 已提交
536
	retire_submits(gpu);
R
Rob Clark 已提交
537 538 539 540 541 542 543 544
	mutex_unlock(&dev->struct_mutex);
}

/* call from irq handler to schedule work to retire bo's */
void msm_gpu_retire(struct msm_gpu *gpu)
{
	struct msm_drm_private *priv = gpu->dev->dev_private;
	queue_work(priv->wq, &gpu->retire_work);
R
Rob Clark 已提交
545
	update_sw_cntrs(gpu);
R
Rob Clark 已提交
546 547 548
}

/* add bo's to gpu's ring, and kick gpu: */
549
void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
R
Rob Clark 已提交
550 551 552 553
		struct msm_file_private *ctx)
{
	struct drm_device *dev = gpu->dev;
	struct msm_drm_private *priv = dev->dev_private;
554
	struct msm_ringbuffer *ring = submit->ring;
555
	int i;
R
Rob Clark 已提交
556

557 558
	WARN_ON(!mutex_is_locked(&dev->struct_mutex));

R
Rob Clark 已提交
559 560 561
	pm_runtime_get_sync(&gpu->pdev->dev);

	msm_gpu_hw_init(gpu);
562

563 564 565
	submit->seqno = ++ring->seqno;

	list_add_tail(&submit->node, &ring->submits);
566

567
	msm_rd_dump_submit(priv->rd, submit, NULL);
R
Rob Clark 已提交
568

R
Rob Clark 已提交
569 570
	update_sw_cntrs(gpu);

R
Rob Clark 已提交
571 572
	for (i = 0; i < submit->nr_bos; i++) {
		struct msm_gem_object *msm_obj = submit->bos[i].obj;
R
Rob Clark 已提交
573
		uint64_t iova;
R
Rob Clark 已提交
574 575 576 577 578 579

		/* can't happen yet.. but when we add 2d support we'll have
		 * to deal w/ cross-ring synchronization:
		 */
		WARN_ON(is_active(msm_obj) && (msm_obj->gpu != gpu));

580 581
		/* submit takes a reference to the bo and iova until retired: */
		drm_gem_object_reference(&msm_obj->base);
582
		msm_gem_get_iova(&msm_obj->base,
583
				submit->gpu->aspace, &iova);
R
Rob Clark 已提交
584

R
Rob Clark 已提交
585 586
		if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
			msm_gem_move_to_active(&msm_obj->base, gpu, true, submit->fence);
R
Rob Clark 已提交
587 588
		else if (submit->bos[i].flags & MSM_SUBMIT_BO_READ)
			msm_gem_move_to_active(&msm_obj->base, gpu, false, submit->fence);
R
Rob Clark 已提交
589
	}
590

591
	gpu->funcs->submit(gpu, submit, ctx);
592 593
	priv->lastctx = ctx;

594
	hangcheck_timer_reset(gpu);
R
Rob Clark 已提交
595 596 597 598 599 600 601 602 603 604 605 606
}

/*
 * Init/Cleanup:
 */

static irqreturn_t irq_handler(int irq, void *data)
{
	struct msm_gpu *gpu = data;
	return gpu->funcs->irq(gpu);
}

607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
static struct clk *get_clock(struct device *dev, const char *name)
{
	struct clk *clk = devm_clk_get(dev, name);

	return IS_ERR(clk) ? NULL : clk;
}

static int get_clocks(struct platform_device *pdev, struct msm_gpu *gpu)
{
	struct device *dev = &pdev->dev;
	struct property *prop;
	const char *name;
	int i = 0;

	gpu->nr_clocks = of_property_count_strings(dev->of_node, "clock-names");
	if (gpu->nr_clocks < 1) {
		gpu->nr_clocks = 0;
		return 0;
	}

	gpu->grp_clks = devm_kcalloc(dev, sizeof(struct clk *), gpu->nr_clocks,
		GFP_KERNEL);
	if (!gpu->grp_clks)
		return -ENOMEM;

	of_property_for_each_string(dev->of_node, "clock-names", prop, name) {
		gpu->grp_clks[i] = get_clock(dev, name);

		/* Remember the key clocks that we need to control later */
636
		if (!strcmp(name, "core") || !strcmp(name, "core_clk"))
637
			gpu->core_clk = gpu->grp_clks[i];
638
		else if (!strcmp(name, "rbbmtimer") || !strcmp(name, "rbbmtimer_clk"))
639 640 641 642 643 644 645
			gpu->rbbmtimer_clk = gpu->grp_clks[i];

		++i;
	}

	return 0;
}
R
Rob Clark 已提交
646

647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
static struct msm_gem_address_space *
msm_gpu_create_address_space(struct msm_gpu *gpu, struct platform_device *pdev,
		uint64_t va_start, uint64_t va_end)
{
	struct iommu_domain *iommu;
	struct msm_gem_address_space *aspace;
	int ret;

	/*
	 * Setup IOMMU.. eventually we will (I think) do this once per context
	 * and have separate page tables per context.  For now, to keep things
	 * simple and to get something working, just use a single address space:
	 */
	iommu = iommu_domain_alloc(&platform_bus_type);
	if (!iommu)
		return NULL;

	iommu->geometry.aperture_start = va_start;
	iommu->geometry.aperture_end = va_end;

	dev_info(gpu->dev->dev, "%s: using IOMMU\n", gpu->name);

	aspace = msm_gem_address_space_create(&pdev->dev, iommu, "gpu");
	if (IS_ERR(aspace)) {
		dev_err(gpu->dev->dev, "failed to init iommu: %ld\n",
			PTR_ERR(aspace));
		iommu_domain_free(iommu);
		return ERR_CAST(aspace);
	}

	ret = aspace->mmu->funcs->attach(aspace->mmu, NULL, 0);
	if (ret) {
		msm_gem_address_space_put(aspace);
		return ERR_PTR(ret);
	}

	return aspace;
}

R
Rob Clark 已提交
686 687
int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
		struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs,
688
		const char *name, struct msm_gpu_config *config)
R
Rob Clark 已提交
689
{
690 691 692
	int i, ret, nr_rings = config->nr_rings;
	void *memptrs;
	uint64_t memptrs_iova;
R
Rob Clark 已提交
693

R
Rob Clark 已提交
694 695 696
	if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs)))
		gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs);

R
Rob Clark 已提交
697 698 699 700 701 702
	gpu->dev = drm;
	gpu->funcs = funcs;
	gpu->name = name;

	INIT_LIST_HEAD(&gpu->active_list);
	INIT_WORK(&gpu->retire_work, retire_worker);
703 704
	INIT_WORK(&gpu->recover_work, recover_worker);

705

706 707
	setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
			(unsigned long)gpu);
R
Rob Clark 已提交
708

R
Rob Clark 已提交
709 710
	spin_lock_init(&gpu->perf_lock);

R
Rob Clark 已提交
711 712

	/* Map registers: */
713
	gpu->mmio = msm_ioremap(pdev, config->ioname, name);
R
Rob Clark 已提交
714 715 716 717 718 719
	if (IS_ERR(gpu->mmio)) {
		ret = PTR_ERR(gpu->mmio);
		goto fail;
	}

	/* Get Interrupt: */
720
	gpu->irq = platform_get_irq_byname(pdev, config->irqname);
R
Rob Clark 已提交
721 722 723 724 725 726 727 728 729 730 731 732 733
	if (gpu->irq < 0) {
		ret = gpu->irq;
		dev_err(drm->dev, "failed to get irq: %d\n", ret);
		goto fail;
	}

	ret = devm_request_irq(&pdev->dev, gpu->irq, irq_handler,
			IRQF_TRIGGER_HIGH, gpu->name, gpu);
	if (ret) {
		dev_err(drm->dev, "failed to request IRQ%u: %d\n", gpu->irq, ret);
		goto fail;
	}

734 735 736
	ret = get_clocks(pdev, gpu);
	if (ret)
		goto fail;
R
Rob Clark 已提交
737

738
	gpu->ebi1_clk = msm_clk_get(pdev, "bus");
R
Rob Clark 已提交
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
	DBG("ebi1_clk: %p", gpu->ebi1_clk);
	if (IS_ERR(gpu->ebi1_clk))
		gpu->ebi1_clk = NULL;

	/* Acquire regulators: */
	gpu->gpu_reg = devm_regulator_get(&pdev->dev, "vdd");
	DBG("gpu_reg: %p", gpu->gpu_reg);
	if (IS_ERR(gpu->gpu_reg))
		gpu->gpu_reg = NULL;

	gpu->gpu_cx = devm_regulator_get(&pdev->dev, "vddcx");
	DBG("gpu_cx: %p", gpu->gpu_cx);
	if (IS_ERR(gpu->gpu_cx))
		gpu->gpu_cx = NULL;

754 755 756 757
	gpu->pdev = pdev;
	platform_set_drvdata(pdev, gpu);

	bs_init(gpu);
758

759 760 761 762
	gpu->aspace = msm_gpu_create_address_space(gpu, pdev,
		config->va_start, config->va_end);

	if (gpu->aspace == NULL)
763
		dev_info(drm->dev, "%s: no IOMMU, fallback to VRAM carveout!\n", name);
764 765 766
	else if (IS_ERR(gpu->aspace)) {
		ret = PTR_ERR(gpu->aspace);
		goto fail;
R
Rob Clark 已提交
767
	}
768

769
	memptrs = msm_gem_kernel_new(drm, sizeof(*gpu->memptrs_bo),
J
Jordan Crouse 已提交
770
		MSM_BO_UNCACHED, gpu->aspace, &gpu->memptrs_bo,
771
		&memptrs_iova);
J
Jordan Crouse 已提交
772

773 774
	if (IS_ERR(memptrs)) {
		ret = PTR_ERR(memptrs);
J
Jordan Crouse 已提交
775 776 777 778
		dev_err(drm->dev, "could not allocate memptrs: %d\n", ret);
		goto fail;
	}

779
	if (nr_rings > ARRAY_SIZE(gpu->rb)) {
780
		DRM_DEV_INFO_ONCE(drm->dev, "Only creating %zu ringbuffers\n",
781 782
			ARRAY_SIZE(gpu->rb));
		nr_rings = ARRAY_SIZE(gpu->rb);
R
Rob Clark 已提交
783 784
	}

785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
	/* Create ringbuffer(s): */
	for (i = 0; i < nr_rings; i++) {
		gpu->rb[i] = msm_ringbuffer_new(gpu, i, memptrs, memptrs_iova);

		if (IS_ERR(gpu->rb[i])) {
			ret = PTR_ERR(gpu->rb[i]);
			dev_err(drm->dev,
				"could not create ringbuffer %d: %d\n", i, ret);
			goto fail;
		}

		memptrs += sizeof(struct msm_rbmemptrs);
		memptrs_iova += sizeof(struct msm_rbmemptrs);
	}

	gpu->nr_rings = nr_rings;

R
Rob Clark 已提交
802 803 804
	return 0;

fail:
805 806 807 808 809
	for (i = 0; i < ARRAY_SIZE(gpu->rb); i++)  {
		msm_ringbuffer_destroy(gpu->rb[i]);
		gpu->rb[i] = NULL;
	}

J
Jordan Crouse 已提交
810 811 812 813 814 815
	if (gpu->memptrs_bo) {
		msm_gem_put_vaddr(gpu->memptrs_bo);
		msm_gem_put_iova(gpu->memptrs_bo, gpu->aspace);
		drm_gem_object_unreference_unlocked(gpu->memptrs_bo);
	}

816
	platform_set_drvdata(pdev, NULL);
R
Rob Clark 已提交
817 818 819 820 821
	return ret;
}

void msm_gpu_cleanup(struct msm_gpu *gpu)
{
822 823
	int i;

R
Rob Clark 已提交
824 825 826 827 828 829
	DBG("%s", gpu->name);

	WARN_ON(!list_empty(&gpu->active_list));

	bs_fini(gpu);

830 831 832
	for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) {
		msm_ringbuffer_destroy(gpu->rb[i]);
		gpu->rb[i] = NULL;
R
Rob Clark 已提交
833
	}
J
Jordan Crouse 已提交
834 835 836 837 838 839 840 841

	if (gpu->memptrs_bo) {
		msm_gem_put_vaddr(gpu->memptrs_bo);
		msm_gem_put_iova(gpu->memptrs_bo, gpu->aspace);
		drm_gem_object_unreference_unlocked(gpu->memptrs_bo);
	}

	if (!IS_ERR_OR_NULL(gpu->aspace)) {
842 843 844 845
		gpu->aspace->mmu->funcs->detach(gpu->aspace->mmu,
			NULL, 0);
		msm_gem_address_space_put(gpu->aspace);
	}
R
Rob Clark 已提交
846
}