blob: 246435d52e97e9e84c44a6434ceac83294121a51 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/protocol/iommu.h>
#include <ddk/protocol/platform-bus.h>
#include <ddk/protocol/platform-defs.h>
#include <ddk/protocol/platform-device.h>
#include <hw/reg.h>
#include <soc/aml-common/aml-gpu.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include <zircon/device/gpu.h>
#include "s912-gpu.h"
#include "s905d2-gpu.h"
static int32_t current_clk_source;
static void aml_gpu_set_clk_freq_source(aml_gpu_t* gpu, int32_t clk_source) {
if (current_clk_source == clk_source) {
return;
}
aml_gpu_block_t* gpu_block = gpu->gpu_block;
uint32_t current_clk_cntl = READ32_HIU_REG(gpu_block->hhi_clock_cntl_offset);
uint32_t enabled_mux = current_clk_cntl & (1 << FINAL_MUX_BIT_SHIFT);
uint32_t new_mux = enabled_mux == 0;
uint32_t mux_shift = new_mux ? 16 : 0;
// clear existing values
current_clk_cntl &= ~(CLOCK_MUX_MASK << mux_shift);
// set the divisor, enable & source for the unused mux
current_clk_cntl |= CALCULATE_CLOCK_MUX(true,
gpu_block->gpu_clk_freq[clk_source], 1) << mux_shift;
// Write the new values to the unused mux
WRITE32_HIU_REG(gpu_block->hhi_clock_cntl_offset, current_clk_cntl);
zx_nanosleep(zx_deadline_after(ZX_USEC(10)));
// Toggle current mux selection
current_clk_cntl ^= (1 << FINAL_MUX_BIT_SHIFT);
// Select the unused input mux
WRITE32_HIU_REG(gpu_block->hhi_clock_cntl_offset, current_clk_cntl);
current_clk_source = clk_source;
}
static void aml_gpu_init(aml_gpu_t* gpu) {
uint32_t temp;
aml_gpu_block_t* gpu_block = gpu->gpu_block;
temp = READ32_PRESET_REG(gpu_block->reset0_mask_offset);
temp &= ~(1 << 20);
WRITE32_PRESET_REG(gpu_block->reset0_mask_offset, temp);
temp = READ32_PRESET_REG(gpu_block->reset0_level_offset);
temp &= ~(1 << 20);
WRITE32_PRESET_REG(gpu_block->reset0_level_offset, temp);
temp = READ32_PRESET_REG(gpu_block->reset2_mask_offset);
temp &= ~(1 << 14);
WRITE32_PRESET_REG(gpu_block->reset2_mask_offset, temp);
temp = READ32_PRESET_REG(gpu_block->reset2_level_offset);
temp &= ~(1 << 14);
WRITE32_PRESET_REG(gpu_block->reset2_level_offset, temp);
// Currently the index 2 corresponds to the default
// value of GPU clock freq which is 500Mhz.
// In future, the GPU driver in garnet
// can make an IOCTL to set the default freq
aml_gpu_set_clk_freq_source(gpu, 2);
temp = READ32_PRESET_REG(gpu_block->reset0_level_offset);
temp |= 1 << 20;
WRITE32_PRESET_REG(gpu_block->reset0_level_offset, temp);
temp = READ32_PRESET_REG(gpu_block->reset2_level_offset);
temp |= 1 << 14;
WRITE32_PRESET_REG(gpu_block->reset2_level_offset, temp);
WRITE32_GPU_REG(PWR_KEY, 0x2968A819);
WRITE32_GPU_REG(PWR_OVERRIDE1, 0xfff | (0x20 << 16));
}
static void aml_gpu_release(void* ctx) {
aml_gpu_t* gpu = ctx;
io_buffer_release(&gpu->hiu_buffer);
io_buffer_release(&gpu->preset_buffer);
io_buffer_release(&gpu->gpu_buffer);
free(gpu);
}
static zx_status_t aml_gpu_get_protocol(void* ctx, uint32_t proto_id, void* out_proto) {
aml_gpu_t* gpu = ctx;
platform_device_protocol_t* gpu_proto = out_proto;
// Forward the underlying ops.
gpu_proto->ops = gpu->pdev.ops;
gpu_proto->ctx = gpu->pdev.ctx;
return ZX_OK;
}
static zx_status_t aml_gpu_ioctl(void* ctx, uint32_t op,
const void* in_buf, size_t in_len,
void* out_buf, size_t out_len,
size_t* out_actual) {
aml_gpu_t* gpu = ctx;
switch(op) {
case IOCTL_GPU_SET_CLK_FREQ_SOURCE: {
if (in_len != sizeof(int32_t)) {
return ZX_ERR_INVALID_ARGS;
}
int32_t *clk_source = (int32_t*)in_buf;
if (*clk_source >= MAX_GPU_CLK_FREQ) {
GPU_ERROR("Invalid clock freq source index\n");
return ZX_ERR_NOT_SUPPORTED;
}
aml_gpu_set_clk_freq_source(gpu, *clk_source);
return ZX_OK;
}
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
static zx_protocol_device_t aml_gpu_protocol = {
.version = DEVICE_OPS_VERSION,
.release = aml_gpu_release,
.get_protocol = aml_gpu_get_protocol,
.ioctl = aml_gpu_ioctl,
};
static zx_status_t aml_gpu_bind(void* ctx, zx_device_t* parent) {
zx_status_t status;
aml_gpu_t* gpu = calloc(1, sizeof(aml_gpu_t));
if (!gpu) {
return ZX_ERR_NO_MEMORY;
}
if ((status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &gpu->pdev)) != ZX_OK) {
GPU_ERROR("ZX_PROTOCOL_PLATFORM_DEV not available\n");
goto fail;
}
status = pdev_map_mmio_buffer(&gpu->pdev, MMIO_GPU, ZX_CACHE_POLICY_UNCACHED_DEVICE,
&gpu->gpu_buffer);
if (status != ZX_OK) {
GPU_ERROR("pdev_map_mmio_buffer failed\n");
goto fail;
}
status = pdev_map_mmio_buffer(&gpu->pdev, MMIO_HIU, ZX_CACHE_POLICY_UNCACHED_DEVICE,
&gpu->hiu_buffer);
if (status != ZX_OK) {
GPU_ERROR("pdev_map_mmio_buffer failed\n");
goto fail;
}
status = pdev_map_mmio_buffer(&gpu->pdev, MMIO_PRESET, ZX_CACHE_POLICY_UNCACHED_DEVICE,
&gpu->preset_buffer);
if (status != ZX_OK) {
GPU_ERROR("pdev_map_mmio_buffer failed\n");
goto fail;
}
pdev_device_info_t info;
status = pdev_get_device_info(&gpu->pdev, &info);
if (status != ZX_OK) {
GPU_ERROR("pdev_get_device_info failed\n");
goto fail;
}
switch (info.pid) {
case PDEV_PID_AMLOGIC_S912:
gpu->gpu_block = &s912_gpu_blocks;
break;
case PDEV_PID_AMLOGIC_S905D2:
gpu->gpu_block = &s905d2_gpu_blocks;
break;
default:
GPU_ERROR("unsupported SOC PID %u\n", info.pid);
goto fail;
}
aml_gpu_init(gpu);
zx_device_prop_t props[] = {
{BIND_PROTOCOL, 0, ZX_PROTOCOL_PLATFORM_DEV},
{BIND_PLATFORM_DEV_VID, 0, PDEV_VID_GENERIC},
{BIND_PLATFORM_DEV_PID, 0, PDEV_PID_GENERIC},
{BIND_PLATFORM_DEV_DID, 0, PDEV_DID_ARM_MALI},
};
device_add_args_t args = {
.version = DEVICE_ADD_ARGS_VERSION,
.name = "aml-gpu",
.ctx = gpu,
.ops = &aml_gpu_protocol,
.props = props,
.prop_count = countof(props),
.proto_id = ZX_PROTOCOL_GPU_THERMAL,
};
status = device_add(parent, &args, &gpu->zxdev);
if (status != ZX_OK) {
goto fail;
}
return ZX_OK;
fail:
aml_gpu_release(gpu);
return status;
}
static zx_driver_ops_t aml_gpu_driver_ops = {
.version = DRIVER_OPS_VERSION,
.bind = aml_gpu_bind,
};
ZIRCON_DRIVER_BEGIN(aml_gpu, aml_gpu_driver_ops, "zircon", "0.1", 5)
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_DEV),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_AMLOGIC),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_DID, PDEV_DID_ARM_MALI_INIT),
// we support multiple SOC variants
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_AMLOGIC_S912),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_AMLOGIC_S905D2),
ZIRCON_DRIVER_END(aml_gpu)