123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #define pr_fmt(fmt) "%s: hw: %s(): " fmt, KBUILD_MODNAME, __func__
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/dma-buf.h>
- #include <linux/slab.h>
- #include <linux/cdev.h>
- #include <linux/qcom_scm.h>
- #include <linux/delay.h>
- #include <asm/barrier.h>
- #include "ubwcp_hw.h"
- static bool ubwcp_hw_trace_en;
- //#define DBG(fmt, args...)
- #define DBG(fmt, args...) \
- do { \
- if (unlikely(ubwcp_hw_trace_en)) \
- pr_err(fmt "\n", ##args); \
- } while (0)
- #define ERR(fmt, args...) pr_err_ratelimited(": %d: ~~~ERROR~~~: " fmt "\n", __LINE__, ##args)
- MODULE_LICENSE("GPL");
- #define PAGE_ADDR_4K(_x) ((_x) >> 12)
- /* register offsets from base */
- #define RANGE_LOWER 0x0000
- #define RANGE_HIGHER 0x0800
- #define DESC_BASE 0x1000
- #define DESC_BASE_STRIDE 0x1004
- #define CONFIG 0x1008
- #define ENCODER_CONFIG 0x100C
- #define ENCODER_STATUS 0x1010
- #define DECODER_CONFIG 0x1014
- #define DECODER_STATUS 0x1018
- #define RANGE_CHECK_FAIL 0x101C
- #define RANGE_CHECK_CONTROL 0x1020
- #define RANGE_CHECK_STATUS 0x1060
- #define FLUSH_CONTROL 0x10A0
- #define FLUSH_STATUS 0x10A4
- #define INTERRUPT_SET 0x10B0
- #define INTERRUPT_STATUS_READ 0x10C0
- #define INTERRUPT_STATUS_WRITE 0x10C4
- #define INTERRUPT_STATUS_ENCODE 0x10C8
- #define INTERRUPT_STATUS_DECODE 0x10CC
- #define INTERRUPT_READ_SRC_LOW 0x1100
- #define INTERRUPT_READ_SRC_HIGH 0x1104
- #define INTERRUPT_WRITE_SRC_LOW 0x1108
- #define INTERRUPT_WRITE_SRC_HIGH 0x110C
- #define INTERRUPT_ENCODE_SRC_LOW 0x1110
- #define INTERRUPT_ENCODE_SRC_HIGH 0x1114
- #define INTERRUPT_DECODE_SRC_LOW 0x1118
- #define INTERRUPT_DECODE_SRC_HIGH 0x111C
- #define INTERRUPT_CLEAR 0x1120
- #define QNS4_PARAMS 0x1124
- #define OVERRIDE 0x112C
- #define VERSION_CONTROL 0x1130
- #define SPARE 0x1188
- #define UBWCP_DEBUG_REG_RW
- /* read/write register */
- #if defined(UBWCP_DEBUG_REG_RW)
- #define UBWCP_REG_READ(_base, _offset) \
- ({u32 _reg; \
- _reg = ioread32(_base + _offset); \
- DBG("READ : 0x%x -> 0x%08x", _offset, _reg); \
- _reg; })
- #define UBWCP_REG_WRITE(_base, _offset, _value) \
- { \
- DBG("WRITE: 0x%x <- 0x%08x", _offset, _value); \
- iowrite32(_value, _base + _offset); \
- }
- #else
- #define UBWCP_REG_READ(_base, _offset) ioread32(_base + _offset)
- #define UBWCP_REG_WRITE(_base, _offset, _value) iowrite32(_value, _base + _offset)
- #endif
- #define UBWCP_REG_READ_NO_DBG(_base, _offset) ioread32(_base + _offset)
- #define UBWCP_REG_WRITE_NO_DBG(_base, _offset, _value) iowrite32(_value, _base + _offset)
- void ubwcp_hw_interrupt_enable(void __iomem *base, u16 interrupt, bool enable)
- {
- u32 value;
- value = UBWCP_REG_READ(base, INTERRUPT_SET);
- if (enable)
- value = value | (1 << interrupt);
- else
- value = value & ~(1 << interrupt);
- UBWCP_REG_WRITE(base, INTERRUPT_SET, value);
- }
- EXPORT_SYMBOL(ubwcp_hw_interrupt_enable);
- void ubwcp_hw_interrupt_clear(void __iomem *base, u16 interrupt)
- {
- UBWCP_REG_WRITE_NO_DBG(base, INTERRUPT_CLEAR, (1 << interrupt));
- }
- EXPORT_SYMBOL(ubwcp_hw_interrupt_clear);
- int ubwcp_hw_interrupt_status(void __iomem *base, u16 interrupt)
- {
- int value = -1;
- switch (interrupt) {
- case INTERRUPT_READ_ERROR:
- value = UBWCP_REG_READ(base, INTERRUPT_STATUS_READ) & 0x1;
- break;
- case INTERRUPT_WRITE_ERROR:
- value = UBWCP_REG_READ(base, INTERRUPT_STATUS_WRITE) & 0x1;
- break;
- case INTERRUPT_DECODE_ERROR:
- value = UBWCP_REG_READ(base, INTERRUPT_STATUS_DECODE) & 0x1;
- break;
- case INTERRUPT_ENCODE_ERROR:
- value = UBWCP_REG_READ(base, INTERRUPT_STATUS_ENCODE) & 0x1;
- break;
- default:
- /* TBD: fatal error? */
- break;
- }
- return value;
- }
- /* returns the address which caused this interrupt */
- u64 ubwcp_hw_interrupt_src_address(void __iomem *base, u16 interrupt)
- {
- u32 addr_low;
- u32 addr_high;
- switch (interrupt) {
- case INTERRUPT_READ_ERROR:
- addr_low = UBWCP_REG_READ_NO_DBG(base, INTERRUPT_READ_SRC_LOW);
- addr_high = UBWCP_REG_READ_NO_DBG(base, INTERRUPT_READ_SRC_HIGH) & 0xF;
- break;
- case INTERRUPT_WRITE_ERROR:
- addr_low = UBWCP_REG_READ_NO_DBG(base, INTERRUPT_WRITE_SRC_LOW);
- addr_high = UBWCP_REG_READ_NO_DBG(base, INTERRUPT_WRITE_SRC_HIGH) & 0xF;
- break;
- case INTERRUPT_DECODE_ERROR:
- addr_low = UBWCP_REG_READ_NO_DBG(base, INTERRUPT_DECODE_SRC_LOW);
- addr_high = UBWCP_REG_READ_NO_DBG(base, INTERRUPT_DECODE_SRC_HIGH) & 0xF;
- break;
- case INTERRUPT_ENCODE_ERROR:
- addr_low = UBWCP_REG_READ_NO_DBG(base, INTERRUPT_ENCODE_SRC_LOW);
- addr_high = UBWCP_REG_READ_NO_DBG(base, INTERRUPT_ENCODE_SRC_HIGH) & 0xF;
- break;
- default:
- /* TBD: fatal error? */
- addr_low = 0x0;
- addr_high = 0x0;
- break;
- }
- return ((addr_high << 31) | addr_low);
- }
- EXPORT_SYMBOL(ubwcp_hw_interrupt_src_address);
- /*
- * @index: index of buffer (from 0 to 255)
- * @pa : ULA PA start address
- * @size : size of ULA PA address range
- */
- void ubwcp_hw_set_range_check(void __iomem *base, u16 index, phys_addr_t pa, size_t size)
- {
- u32 lower;
- u32 higher;
- lower = PAGE_ADDR_4K(pa);
- higher = PAGE_ADDR_4K(pa + size);
- UBWCP_REG_WRITE(base, RANGE_LOWER + index*4, lower);
- UBWCP_REG_WRITE(base, RANGE_HIGHER + index*4, higher);
- }
- EXPORT_SYMBOL(ubwcp_hw_set_range_check);
- /* enable range ck:
- * identify control register for this index.
- * 32bits in each ctrl reg. upto 8 regs for 256 indexes
- */
- void ubwcp_hw_enable_range_check(void __iomem *base, u16 index)
- {
- u32 val;
- u16 ctrl_reg = index >> 5;
- val = UBWCP_REG_READ(base, RANGE_CHECK_CONTROL + ctrl_reg*4);
- val |= (1 << (index & 0x1F));
- UBWCP_REG_WRITE(base, RANGE_CHECK_CONTROL + ctrl_reg*4, val);
- }
- EXPORT_SYMBOL(ubwcp_hw_enable_range_check);
- /* Disable range check with flush */
- int ubwcp_hw_disable_range_check_with_flush(void __iomem *base, u16 index)
- {
- u32 flush_complete = 0;
- u32 count = 20;
- u32 val;
- u16 ctrl_reg = index >> 5;
- /*
- * It is not clear that the isb() calls in this sequence are
- * requried, we may be able to remove them.
- */
- //ensure all CMOs have completed
- isb();
- //disable range ck
- val = UBWCP_REG_READ(base, RANGE_CHECK_CONTROL + ctrl_reg*4);
- val &= ~(1 << (index & 0x1F));
- UBWCP_REG_WRITE(base, RANGE_CHECK_CONTROL + ctrl_reg*4, val);
- isb();
- //assert flush
- UBWCP_REG_WRITE(base, FLUSH_CONTROL, 0x3);
- //poll for flush done
- do {
- flush_complete = UBWCP_REG_READ(base, FLUSH_STATUS) & 0x1;
- if (flush_complete) {
- //clear flush
- UBWCP_REG_WRITE(base, FLUSH_CONTROL, 0x0);
- return 0;
- }
- udelay(100);
- } while (count--);
- ERR("~~~~~ FLUSH FAILED ~~~~~");
- return -1;
- }
- EXPORT_SYMBOL(ubwcp_hw_disable_range_check_with_flush);
- void ubwcp_hw_set_buf_desc(void __iomem *base, u64 desc_addr, u16 desc_stride)
- {
- UBWCP_REG_WRITE(base, DESC_BASE, PAGE_ADDR_4K(desc_addr));
- UBWCP_REG_WRITE(base, DESC_BASE_STRIDE, desc_stride);
- }
- EXPORT_SYMBOL(ubwcp_hw_set_buf_desc);
- /* Value set here is returned upon read of an address that fails range check.
- * Writes are ignored.
- * Will also generate range_check_fail interrupt if enabled.
- * if we don't program, default value is: 0x92929292
- */
- void ubwcp_hw_set_default_range_check_value(void __iomem *base, u32 val)
- {
- UBWCP_REG_WRITE(base, RANGE_CHECK_FAIL, val);
- }
- void ubwcp_hw_version(void __iomem *base, u32 *major, u32 *minor)
- {
- u32 version;
- version = UBWCP_REG_READ(base, VERSION_CONTROL);
- *major = version & 0xF;
- *minor = (version & 0xF0) >> 4;
- }
- EXPORT_SYMBOL(ubwcp_hw_version);
- /* TBD: */
- void ubwcp_hw_macro_tile_config(void __iomem *base)
- {
- //TODO: In future add in support for LP4
- //May be able to determine DDR version via call to
- //of_fdt_get_ddrtype()
- /*
- * For Lanai assume 4 Channel LP5 DDR so from HSR
- * MAL Size 32B
- * Highest Bank Bit 16
- * Level 1 Bank Swizzling Disable
- * Level 2 Bank Swizzling Enable
- * Level 3 Bank Swizzling Enable
- * Bank Spreading Enable
- * Macrotiling Configuration (Num Channels) 8
- */
- UBWCP_REG_WRITE(base, CONFIG, 0x1E3);
- }
- /* TBD: */
- void ubwcp_hw_decoder_config(void __iomem *base)
- {
- /*
- * For Lanai assume AMSBC (UBWC4.4/4.3) algorithm is used == b11
- * For Lanai assume 4 Channel LP5 DDR so MAL Size 32B == b0
- */
- UBWCP_REG_WRITE(base, DECODER_CONFIG, 0x7);
- }
- /* TBD: */
- void ubwcp_hw_encoder_config(void __iomem *base)
- {
- /*
- * For Lanai assume AMSBC (UBWC4.4/4.3) algorithm is used == b11
- * For Lanai assume 4 Channel LP5 DDR so MAL Size 32B == b0
- */
- UBWCP_REG_WRITE(base, ENCODER_CONFIG, 0x7);
- }
- int ubwcp_hw_flush(void __iomem *base)
- {
- u32 flush_complete = 0;
- u32 count = 20;
- UBWCP_REG_WRITE(base, FLUSH_CONTROL, 0x3);
- do {
- flush_complete = UBWCP_REG_READ(base, FLUSH_STATUS) & 0x1;
- if (flush_complete) {
- UBWCP_REG_WRITE(base, FLUSH_CONTROL, 0x0);
- return 0;
- }
- udelay(100);
- } while (count--);
- ERR("~~~~~ FLUSH FAILED ~~~~~");
- return -1;
- }
- EXPORT_SYMBOL(ubwcp_hw_flush);
- void ubwcp_hw_power_vote_status(void __iomem *pwr_ctrl, u8 *vote, u8 *status)
- {
- u32 reg;
- reg = UBWCP_REG_READ(pwr_ctrl, 0);
- *vote = (reg & BIT(0)) >> 0;
- *status = (reg & BIT(31)) >> 31;
- }
- void ubwcp_hw_one_time_init(void __iomem *base)
- {
- u32 reg;
- /* Spare reg config: set bit-9: SCC & bit-1: padding */
- reg = UBWCP_REG_READ(base, SPARE);
- reg |= BIT(9) | BIT(1);
- UBWCP_REG_WRITE(base, SPARE, reg);
- /* Configure SID */
- reg = UBWCP_REG_READ(base, QNS4_PARAMS);
- reg &= ~(0x3F);
- reg |= 0x1; /* desc buffer */
- reg |= (0 << 3); /* pixel data */
- UBWCP_REG_WRITE(base, QNS4_PARAMS, reg);
- ubwcp_hw_decoder_config(base);
- ubwcp_hw_encoder_config(base);
- ubwcp_hw_macro_tile_config(base);
- }
- EXPORT_SYMBOL(ubwcp_hw_one_time_init);
- void ubwcp_hw_trace_set(bool value)
- {
- ubwcp_hw_trace_en = value;
- }
- EXPORT_SYMBOL(ubwcp_hw_trace_set);
- void ubwcp_hw_trace_get(bool *value)
- {
- *value = ubwcp_hw_trace_en;
- }
- EXPORT_SYMBOL(ubwcp_hw_trace_get);
|