[gpu][amlogic] Add IOCTLs for changing GPU clock freq.

TEST- Ran GPU tests to ensure nothing broke
Change-Id: I9fb9d52bfdbeaca3e75021e2e5112bdecb6d04e0
diff --git a/system/dev/gpu/aml-gpu/aml-gpu.c b/system/dev/gpu/aml-gpu/aml-gpu.c
index bb2070f..9cd1af7 100644
--- a/system/dev/gpu/aml-gpu/aml-gpu.c
+++ b/system/dev/gpu/aml-gpu/aml-gpu.c
@@ -19,10 +19,34 @@
 #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 void aml_gpu_set_clk_freq_source(aml_gpu_t* gpu, int32_t clk_source) {
+    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);
+}
+
 static void aml_gpu_init(aml_gpu_t* gpu) {
     uint32_t temp;
     aml_gpu_block_t* gpu_block = gpu->gpu_block;
@@ -43,8 +67,11 @@
     temp &= ~(1 << 14);
     WRITE32_PRESET_REG(gpu_block->reset2_level_offset, temp);
 
-    WRITE32_HIU_REG(gpu_block->hhi_clock_cntl_offset, gpu_block->mhz500);
-    zx_nanosleep(zx_deadline_after(ZX_USEC(500)));
+    // 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;
@@ -74,14 +101,38 @@
     // 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) {
diff --git a/system/dev/gpu/aml-gpu/s905d2-gpu.h b/system/dev/gpu/aml-gpu/s905d2-gpu.h
index c8f548f..56c80f0 100644
--- a/system/dev/gpu/aml-gpu/s905d2-gpu.h
+++ b/system/dev/gpu/aml-gpu/s905d2-gpu.h
@@ -6,9 +6,6 @@
 
 #include <soc/aml-s905d2/s905d2-hw.h>
 
-#define CALCULATE_CLOCK(enabled, base, divisor) \
-        ((!!(enabled) << 8) | (base << 9) | (divisor - 1))
-
 enum {
     S905D2_XTAL        = 0,    // 24MHz
     S905D2_GP0         = 1,
@@ -26,5 +23,12 @@
     .reset2_level_offset        = S905D2_RESET2_LEVEL,
     .reset2_mask_offset         = S905D2_RESET2_MASK,
     .hhi_clock_cntl_offset      = 0x6C,
-    .mhz500                     = CALCULATE_CLOCK(true, S905D2_FCLK_DIV4, 1),
+    .gpu_clk_freq               =
+    {
+        S905D2_FCLK_DIV7,
+        S905D2_FCLK_DIV5,
+        S905D2_FCLK_DIV4,
+        S905D2_FCLK_DIV3,
+        S905D2_FCLK_DIV2P5,
+    },
 };
diff --git a/system/dev/gpu/aml-gpu/s912-gpu.h b/system/dev/gpu/aml-gpu/s912-gpu.h
index 57b3351..e759629 100644
--- a/system/dev/gpu/aml-gpu/s912-gpu.h
+++ b/system/dev/gpu/aml-gpu/s912-gpu.h
@@ -6,8 +6,6 @@
 
 #include <soc/aml-s912/s912-hw.h>
 
-#define CALCULATE_CLOCK(enabled, base, divisor) \
-        ((!!(enabled) << 8) | (base << 9) | (divisor - 1))
 
 enum {
     S912_XTAL        = 0, // 25MHz
@@ -26,5 +24,11 @@
     .reset2_level_offset        = S912_RESET2_LEVEL,
     .reset2_mask_offset         = S912_RESET2_MASK,
     .hhi_clock_cntl_offset      = 0x6C,
-    .mhz500                     = CALCULATE_CLOCK(true, S912_FCLK_DIV4, 1),
+    .gpu_clk_freq               =
+    {
+        S912_FCLK_DIV7,
+        S912_FCLK_DIV5,
+        S912_FCLK_DIV4,
+        S912_FCLK_DIV3,
+    },
 };
diff --git a/system/dev/lib/amlogic/include/soc/aml-common/aml-gpu.h b/system/dev/lib/amlogic/include/soc/aml-common/aml-gpu.h
index 581fd71..34f38a2 100644
--- a/system/dev/lib/amlogic/include/soc/aml-common/aml-gpu.h
+++ b/system/dev/lib/amlogic/include/soc/aml-common/aml-gpu.h
@@ -34,9 +34,14 @@
 #define WRITE32_PRESET_REG(offset, value)  writel(value, io_buffer_virt(&gpu->preset_buffer) \
                                            + offset*4)
 
-#define CALCULATE_CLOCK(enabled, base, divisor) \
+#define CALCULATE_CLOCK_MUX(enabled, base, divisor) \
         ((!!(enabled) << 8) | (base << 9) | (divisor - 1))
 
+#define CLOCK_MUX_MASK                  0xFFF
+
+#define MAX_GPU_CLK_FREQ                5
+#define FINAL_MUX_BIT_SHIFT             31
+
 enum {
     MMIO_GPU,
     MMIO_HIU,
@@ -49,7 +54,7 @@
     uint32_t reset2_level_offset;
     uint32_t reset2_mask_offset;
     uint32_t hhi_clock_cntl_offset;
-    uint32_t mhz500;
+    uint32_t gpu_clk_freq[MAX_GPU_CLK_FREQ];
 }aml_gpu_block_t;
 
 typedef struct {
diff --git a/system/public/zircon/device/gpu.h b/system/public/zircon/device/gpu.h
new file mode 100644
index 0000000..8d7de0f
--- /dev/null
+++ b/system/public/zircon/device/gpu.h
@@ -0,0 +1,15 @@
+// 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.
+
+#pragma once
+
+#include <zircon/device/ioctl.h>
+#include <zircon/device/ioctl-wrapper.h>
+
+// Sets the GPU  clock freq. source
+#define IOCTL_GPU_SET_CLK_FREQ_SOURCE \
+        IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_GPU, 1)
+
+// ssize_t ioctl_gpu_set_freq_source(int fd, uint32_t clk_source)
+IOCTL_WRAPPER_IN(ioctl_gpu_set_clk_freq_source, IOCTL_GPU_SET_CLK_FREQ_SOURCE, int32_t);