diff options
author | Mauro Carvalho Chehab <mchehab+huawei@kernel.org> | 2020-04-15 12:03:40 +0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab+huawei@kernel.org> | 2020-04-15 13:06:40 +0300 |
commit | 4be5e8648b0c287aefc6ac3f3a0b12c696054f43 (patch) | |
tree | 7b71f97912a07048092cb7827e9bb8f17f874482 /drivers/media/platform/meson | |
parent | 46d2a3b964ddbe63605dab502c847180b1efbfb2 (diff) | |
download | linux-4be5e8648b0c287aefc6ac3f3a0b12c696054f43.tar.xz |
media: move CEC platform drivers to a separate directory
As CEC support doesn't depend on MEDIA_SUPPORT, let's
place the platform drivers outside the media menu.
As a side effect, instead of depends on PCI, seco driver
can select it (and DMI).
Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Diffstat (limited to 'drivers/media/platform/meson')
-rw-r--r-- | drivers/media/platform/meson/Makefile | 3 | ||||
-rw-r--r-- | drivers/media/platform/meson/ao-cec-g12a.c | 796 | ||||
-rw-r--r-- | drivers/media/platform/meson/ao-cec.c | 732 |
3 files changed, 0 insertions, 1531 deletions
diff --git a/drivers/media/platform/meson/Makefile b/drivers/media/platform/meson/Makefile deleted file mode 100644 index 6bf728addbf8..000000000000 --- a/drivers/media/platform/meson/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VIDEO_MESON_AO_CEC) += ao-cec.o -obj-$(CONFIG_VIDEO_MESON_G12A_AO_CEC) += ao-cec-g12a.o diff --git a/drivers/media/platform/meson/ao-cec-g12a.c b/drivers/media/platform/meson/ao-cec-g12a.c deleted file mode 100644 index 891533060d49..000000000000 --- a/drivers/media/platform/meson/ao-cec-g12a.c +++ /dev/null @@ -1,796 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for Amlogic Meson AO CEC G12A Controller - * - * Copyright (C) 2017 Amlogic, Inc. All rights reserved - * Copyright (C) 2019 BayLibre, SAS - * Author: Neil Armstrong <narmstrong@baylibre.com> - */ - -#include <linux/bitfield.h> -#include <linux/clk.h> -#include <linux/device.h> -#include <linux/io.h> -#include <linux/delay.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_platform.h> -#include <linux/platform_device.h> -#include <linux/types.h> -#include <linux/interrupt.h> -#include <linux/reset.h> -#include <linux/slab.h> -#include <linux/regmap.h> -#include <media/cec.h> -#include <media/cec-notifier.h> -#include <linux/clk-provider.h> - -/* CEC Registers */ - -#define CECB_CLK_CNTL_REG0 0x00 - -#define CECB_CLK_CNTL_N1 GENMASK(11, 0) -#define CECB_CLK_CNTL_N2 GENMASK(23, 12) -#define CECB_CLK_CNTL_DUAL_EN BIT(28) -#define CECB_CLK_CNTL_OUTPUT_EN BIT(30) -#define CECB_CLK_CNTL_INPUT_EN BIT(31) - -#define CECB_CLK_CNTL_REG1 0x04 - -#define CECB_CLK_CNTL_M1 GENMASK(11, 0) -#define CECB_CLK_CNTL_M2 GENMASK(23, 12) -#define CECB_CLK_CNTL_BYPASS_EN BIT(24) - -/* - * [14:12] Filter_del. For glitch-filtering CEC line, ignore signal - * change pulse width < filter_del * T(filter_tick) * 3. - * [9:8] Filter_tick_sel: Select which periodical pulse for - * glitch-filtering CEC line signal. - * - 0=Use T(xtal)*3 = 125ns; - * - 1=Use once-per-1us pulse; - * - 2=Use once-per-10us pulse; - * - 3=Use once-per-100us pulse. - * [3] Sysclk_en. 0=Disable system clock; 1=Enable system clock. - * [2:1] cntl_clk - * - 0 = Disable clk (Power-off mode) - * - 1 = Enable gated clock (Normal mode) - * - 2 = Enable free-run clk (Debug mode) - * [0] SW_RESET 1=Apply reset; 0=No reset. - */ -#define CECB_GEN_CNTL_REG 0x08 - -#define CECB_GEN_CNTL_RESET BIT(0) -#define CECB_GEN_CNTL_CLK_DISABLE 0 -#define CECB_GEN_CNTL_CLK_ENABLE 1 -#define CECB_GEN_CNTL_CLK_ENABLE_DBG 2 -#define CECB_GEN_CNTL_CLK_CTRL_MASK GENMASK(2, 1) -#define CECB_GEN_CNTL_SYS_CLK_EN BIT(3) -#define CECB_GEN_CNTL_FILTER_TICK_125NS 0 -#define CECB_GEN_CNTL_FILTER_TICK_1US 1 -#define CECB_GEN_CNTL_FILTER_TICK_10US 2 -#define CECB_GEN_CNTL_FILTER_TICK_100US 3 -#define CECB_GEN_CNTL_FILTER_TICK_SEL GENMASK(9, 8) -#define CECB_GEN_CNTL_FILTER_DEL GENMASK(14, 12) - -/* - * [7:0] cec_reg_addr - * [15:8] cec_reg_wrdata - * [16] cec_reg_wr - * - 0 = Read - * - 1 = Write - * [31:24] cec_reg_rddata - */ -#define CECB_RW_REG 0x0c - -#define CECB_RW_ADDR GENMASK(7, 0) -#define CECB_RW_WR_DATA GENMASK(15, 8) -#define CECB_RW_WRITE_EN BIT(16) -#define CECB_RW_BUS_BUSY BIT(23) -#define CECB_RW_RD_DATA GENMASK(31, 24) - -/* - * [0] DONE Interrupt - * [1] End Of Message Interrupt - * [2] Not Acknowlegde Interrupt - * [3] Arbitration Loss Interrupt - * [4] Initiator Error Interrupt - * [5] Follower Error Interrupt - * [6] Wake-Up Interrupt - */ -#define CECB_INTR_MASKN_REG 0x10 -#define CECB_INTR_CLR_REG 0x14 -#define CECB_INTR_STAT_REG 0x18 - -#define CECB_INTR_DONE BIT(0) -#define CECB_INTR_EOM BIT(1) -#define CECB_INTR_NACK BIT(2) -#define CECB_INTR_ARB_LOSS BIT(3) -#define CECB_INTR_INITIATOR_ERR BIT(4) -#define CECB_INTR_FOLLOWER_ERR BIT(5) -#define CECB_INTR_WAKE_UP BIT(6) - -/* CEC Commands */ - -#define CECB_CTRL 0x00 - -#define CECB_CTRL_SEND BIT(0) -#define CECB_CTRL_TYPE GENMASK(2, 1) -#define CECB_CTRL_TYPE_RETRY 0 -#define CECB_CTRL_TYPE_NEW 1 -#define CECB_CTRL_TYPE_NEXT 2 - -#define CECB_CTRL2 0x01 - -#define CECB_CTRL2_RISE_DEL_MAX GENMASK(4, 0) - -#define CECB_INTR_MASK 0x02 -#define CECB_LADD_LOW 0x05 -#define CECB_LADD_HIGH 0x06 -#define CECB_TX_CNT 0x07 -#define CECB_RX_CNT 0x08 -#define CECB_STAT0 0x09 -#define CECB_TX_DATA00 0x10 -#define CECB_TX_DATA01 0x11 -#define CECB_TX_DATA02 0x12 -#define CECB_TX_DATA03 0x13 -#define CECB_TX_DATA04 0x14 -#define CECB_TX_DATA05 0x15 -#define CECB_TX_DATA06 0x16 -#define CECB_TX_DATA07 0x17 -#define CECB_TX_DATA08 0x18 -#define CECB_TX_DATA09 0x19 -#define CECB_TX_DATA10 0x1A -#define CECB_TX_DATA11 0x1B -#define CECB_TX_DATA12 0x1C -#define CECB_TX_DATA13 0x1D -#define CECB_TX_DATA14 0x1E -#define CECB_TX_DATA15 0x1F -#define CECB_RX_DATA00 0x20 -#define CECB_RX_DATA01 0x21 -#define CECB_RX_DATA02 0x22 -#define CECB_RX_DATA03 0x23 -#define CECB_RX_DATA04 0x24 -#define CECB_RX_DATA05 0x25 -#define CECB_RX_DATA06 0x26 -#define CECB_RX_DATA07 0x27 -#define CECB_RX_DATA08 0x28 -#define CECB_RX_DATA09 0x29 -#define CECB_RX_DATA10 0x2A -#define CECB_RX_DATA11 0x2B -#define CECB_RX_DATA12 0x2C -#define CECB_RX_DATA13 0x2D -#define CECB_RX_DATA14 0x2E -#define CECB_RX_DATA15 0x2F -#define CECB_LOCK_BUF 0x30 - -#define CECB_LOCK_BUF_EN BIT(0) - -#define CECB_WAKEUPCTRL 0x31 - -struct meson_ao_cec_g12a_data { - /* Setup the internal CECB_CTRL2 register */ - bool ctrl2_setup; -}; - -struct meson_ao_cec_g12a_device { - struct platform_device *pdev; - struct regmap *regmap; - struct regmap *regmap_cec; - spinlock_t cec_reg_lock; - struct cec_notifier *notify; - struct cec_adapter *adap; - struct cec_msg rx_msg; - struct clk *oscin; - struct clk *core; - const struct meson_ao_cec_g12a_data *data; -}; - -static const struct regmap_config meson_ao_cec_g12a_regmap_conf = { - .reg_bits = 8, - .val_bits = 32, - .reg_stride = 4, - .max_register = CECB_INTR_STAT_REG, -}; - -/* - * The AO-CECB embeds a dual/divider to generate a more precise - * 32,768KHz clock for CEC core clock. - * ______ ______ - * | | | | - * ______ | Div1 |-| Cnt1 | ______ - * | | /|______| |______|\ | | - * Xtal-->| Gate |---| ______ ______ X-X--| Gate |--> - * |______| | \| | | |/ | |______| - * | | Div2 |-| Cnt2 | | - * | |______| |______| | - * |_______________________| - * - * The dividing can be switched to single or dual, with a counter - * for each divider to set when the switching is done. - * The entire dividing mechanism can be also bypassed. - */ - -struct meson_ao_cec_g12a_dualdiv_clk { - struct clk_hw hw; - struct regmap *regmap; -}; - -#define hw_to_meson_ao_cec_g12a_dualdiv_clk(_hw) \ - container_of(_hw, struct meson_ao_cec_g12a_dualdiv_clk, hw) \ - -static unsigned long -meson_ao_cec_g12a_dualdiv_clk_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk = - hw_to_meson_ao_cec_g12a_dualdiv_clk(hw); - unsigned long n1; - u32 reg0, reg1; - - regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, ®0); - regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, ®1); - - if (reg1 & CECB_CLK_CNTL_BYPASS_EN) - return parent_rate; - - if (reg0 & CECB_CLK_CNTL_DUAL_EN) { - unsigned long n2, m1, m2, f1, f2, p1, p2; - - n1 = FIELD_GET(CECB_CLK_CNTL_N1, reg0) + 1; - n2 = FIELD_GET(CECB_CLK_CNTL_N2, reg0) + 1; - - m1 = FIELD_GET(CECB_CLK_CNTL_M1, reg1) + 1; - m2 = FIELD_GET(CECB_CLK_CNTL_M1, reg1) + 1; - - f1 = DIV_ROUND_CLOSEST(parent_rate, n1); - f2 = DIV_ROUND_CLOSEST(parent_rate, n2); - - p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2)); - p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2)); - - return DIV_ROUND_UP(100000000, p1 + p2); - } - - n1 = FIELD_GET(CECB_CLK_CNTL_N1, reg0) + 1; - - return DIV_ROUND_CLOSEST(parent_rate, n1); -} - -static int meson_ao_cec_g12a_dualdiv_clk_enable(struct clk_hw *hw) -{ - struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk = - hw_to_meson_ao_cec_g12a_dualdiv_clk(hw); - - - /* Disable Input & Output */ - regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, - CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN, - 0); - - /* Set N1 & N2 */ - regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, - CECB_CLK_CNTL_N1, - FIELD_PREP(CECB_CLK_CNTL_N1, 733 - 1)); - - regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, - CECB_CLK_CNTL_N2, - FIELD_PREP(CECB_CLK_CNTL_N2, 732 - 1)); - - /* Set M1 & M2 */ - regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1, - CECB_CLK_CNTL_M1, - FIELD_PREP(CECB_CLK_CNTL_M1, 8 - 1)); - - regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1, - CECB_CLK_CNTL_M2, - FIELD_PREP(CECB_CLK_CNTL_M2, 11 - 1)); - - /* Enable Dual divisor */ - regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, - CECB_CLK_CNTL_DUAL_EN, CECB_CLK_CNTL_DUAL_EN); - - /* Disable divisor bypass */ - regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG1, - CECB_CLK_CNTL_BYPASS_EN, 0); - - /* Enable Input & Output */ - regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, - CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN, - CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN); - - return 0; -} - -static void meson_ao_cec_g12a_dualdiv_clk_disable(struct clk_hw *hw) -{ - struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk = - hw_to_meson_ao_cec_g12a_dualdiv_clk(hw); - - regmap_update_bits(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, - CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN, - 0); -} - -static int meson_ao_cec_g12a_dualdiv_clk_is_enabled(struct clk_hw *hw) -{ - struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk = - hw_to_meson_ao_cec_g12a_dualdiv_clk(hw); - int val; - - regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, &val); - - return !!(val & (CECB_CLK_CNTL_INPUT_EN | CECB_CLK_CNTL_OUTPUT_EN)); -} - -static const struct clk_ops meson_ao_cec_g12a_dualdiv_clk_ops = { - .recalc_rate = meson_ao_cec_g12a_dualdiv_clk_recalc_rate, - .is_enabled = meson_ao_cec_g12a_dualdiv_clk_is_enabled, - .enable = meson_ao_cec_g12a_dualdiv_clk_enable, - .disable = meson_ao_cec_g12a_dualdiv_clk_disable, -}; - -static int meson_ao_cec_g12a_setup_clk(struct meson_ao_cec_g12a_device *ao_cec) -{ - struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk; - struct device *dev = &ao_cec->pdev->dev; - struct clk_init_data init; - const char *parent_name; - struct clk *clk; - char *name; - - dualdiv_clk = devm_kzalloc(dev, sizeof(*dualdiv_clk), GFP_KERNEL); - if (!dualdiv_clk) - return -ENOMEM; - - name = kasprintf(GFP_KERNEL, "%s#dualdiv_clk", dev_name(dev)); - if (!name) - return -ENOMEM; - - parent_name = __clk_get_name(ao_cec->oscin); - - init.name = name; - init.ops = &meson_ao_cec_g12a_dualdiv_clk_ops; - init.flags = 0; - init.parent_names = &parent_name; - init.num_parents = 1; - dualdiv_clk->regmap = ao_cec->regmap; - dualdiv_clk->hw.init = &init; - - clk = devm_clk_register(dev, &dualdiv_clk->hw); - kfree(name); - if (IS_ERR(clk)) { - dev_err(dev, "failed to register clock\n"); - return PTR_ERR(clk); - } - - ao_cec->core = clk; - - return 0; -} - -static int meson_ao_cec_g12a_read(void *context, unsigned int addr, - unsigned int *data) -{ - struct meson_ao_cec_g12a_device *ao_cec = context; - u32 reg = FIELD_PREP(CECB_RW_ADDR, addr); - int ret = 0; - - ret = regmap_write(ao_cec->regmap, CECB_RW_REG, reg); - if (ret) - return ret; - - ret = regmap_read_poll_timeout(ao_cec->regmap, CECB_RW_REG, reg, - !(reg & CECB_RW_BUS_BUSY), - 5, 1000); - if (ret) - return ret; - - ret = regmap_read(ao_cec->regmap, CECB_RW_REG, ®); - - *data = FIELD_GET(CECB_RW_RD_DATA, reg); - - return ret; -} - -static int meson_ao_cec_g12a_write(void *context, unsigned int addr, - unsigned int data) -{ - struct meson_ao_cec_g12a_device *ao_cec = context; - u32 reg = FIELD_PREP(CECB_RW_ADDR, addr) | - FIELD_PREP(CECB_RW_WR_DATA, data) | - CECB_RW_WRITE_EN; - - return regmap_write(ao_cec->regmap, CECB_RW_REG, reg); -} - -static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = { - .reg_bits = 8, - .val_bits = 8, - .reg_read = meson_ao_cec_g12a_read, - .reg_write = meson_ao_cec_g12a_write, - .max_register = 0xffff, -}; - -static inline void -meson_ao_cec_g12a_irq_setup(struct meson_ao_cec_g12a_device *ao_cec, - bool enable) -{ - u32 cfg = CECB_INTR_DONE | CECB_INTR_EOM | CECB_INTR_NACK | - CECB_INTR_ARB_LOSS | CECB_INTR_INITIATOR_ERR | - CECB_INTR_FOLLOWER_ERR; - - regmap_write(ao_cec->regmap, CECB_INTR_MASKN_REG, - enable ? cfg : 0); -} - -static void meson_ao_cec_g12a_irq_rx(struct meson_ao_cec_g12a_device *ao_cec) -{ - int i, ret = 0; - u32 val; - - ret = regmap_read(ao_cec->regmap_cec, CECB_RX_CNT, &val); - - ao_cec->rx_msg.len = val; - if (ao_cec->rx_msg.len > CEC_MAX_MSG_SIZE) - ao_cec->rx_msg.len = CEC_MAX_MSG_SIZE; - - for (i = 0; i < ao_cec->rx_msg.len; i++) { - ret |= regmap_read(ao_cec->regmap_cec, - CECB_RX_DATA00 + i, &val); - - ao_cec->rx_msg.msg[i] = val & 0xff; - } - - ret |= regmap_write(ao_cec->regmap_cec, CECB_LOCK_BUF, 0); - if (ret) - return; - - cec_received_msg(ao_cec->adap, &ao_cec->rx_msg); -} - -static irqreturn_t meson_ao_cec_g12a_irq(int irq, void *data) -{ - struct meson_ao_cec_g12a_device *ao_cec = data; - u32 stat; - - regmap_read(ao_cec->regmap, CECB_INTR_STAT_REG, &stat); - if (stat) - return IRQ_WAKE_THREAD; - - return IRQ_NONE; -} - -static irqreturn_t meson_ao_cec_g12a_irq_thread(int irq, void *data) -{ - struct meson_ao_cec_g12a_device *ao_cec = data; - u32 stat; - - regmap_read(ao_cec->regmap, CECB_INTR_STAT_REG, &stat); - regmap_write(ao_cec->regmap, CECB_INTR_CLR_REG, stat); - - if (stat & CECB_INTR_DONE) - cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_OK); - - if (stat & CECB_INTR_EOM) - meson_ao_cec_g12a_irq_rx(ao_cec); - - if (stat & CECB_INTR_NACK) - cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_NACK); - - if (stat & CECB_INTR_ARB_LOSS) { - regmap_write(ao_cec->regmap_cec, CECB_TX_CNT, 0); - regmap_update_bits(ao_cec->regmap_cec, CECB_CTRL, - CECB_CTRL_SEND | CECB_CTRL_TYPE, 0); - cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ARB_LOST); - } - - /* Initiator reports an error on the CEC bus */ - if (stat & CECB_INTR_INITIATOR_ERR) - cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ERROR); - - /* Follower reports a receive error, just reset RX buffer */ - if (stat & CECB_INTR_FOLLOWER_ERR) - regmap_write(ao_cec->regmap_cec, CECB_LOCK_BUF, 0); - - return IRQ_HANDLED; -} - -static int -meson_ao_cec_g12a_set_log_addr(struct cec_adapter *adap, u8 logical_addr) -{ - struct meson_ao_cec_g12a_device *ao_cec = adap->priv; - int ret = 0; - - if (logical_addr == CEC_LOG_ADDR_INVALID) { - /* Assume this will allways succeed */ - regmap_write(ao_cec->regmap_cec, CECB_LADD_LOW, 0); - regmap_write(ao_cec->regmap_cec, CECB_LADD_HIGH, 0); - - return 0; - } else if (logical_addr < 8) { - ret = regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_LOW, - BIT(logical_addr), - BIT(logical_addr)); - } else { - ret = regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_HIGH, - BIT(logical_addr - 8), - BIT(logical_addr - 8)); - } - - /* Always set Broadcast/Unregistered 15 address */ - ret |= regmap_update_bits(ao_cec->regmap_cec, CECB_LADD_HIGH, - BIT(CEC_LOG_ADDR_UNREGISTERED - 8), - BIT(CEC_LOG_ADDR_UNREGISTERED - 8)); - - return ret ? -EIO : 0; -} - -static int meson_ao_cec_g12a_transmit(struct cec_adapter *adap, u8 attempts, - u32 signal_free_time, struct cec_msg *msg) -{ - struct meson_ao_cec_g12a_device *ao_cec = adap->priv; - unsigned int type; - int ret = 0; - u32 val; - int i; - - /* Check if RX is in progress */ - ret = regmap_read(ao_cec->regmap_cec, CECB_LOCK_BUF, &val); - if (ret) - return ret; - if (val & CECB_LOCK_BUF_EN) - return -EBUSY; - - /* Check if TX Busy */ - ret = regmap_read(ao_cec->regmap_cec, CECB_CTRL, &val); - if (ret) - return ret; - if (val & CECB_CTRL_SEND) - return -EBUSY; - - switch (signal_free_time) { - case CEC_SIGNAL_FREE_TIME_RETRY: - type = CECB_CTRL_TYPE_RETRY; - break; - case CEC_SIGNAL_FREE_TIME_NEXT_XFER: - type = CECB_CTRL_TYPE_NEXT; - break; - case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR: - default: - type = CECB_CTRL_TYPE_NEW; - break; - } - - for (i = 0; i < msg->len; i++) - ret |= regmap_write(ao_cec->regmap_cec, CECB_TX_DATA00 + i, - msg->msg[i]); - - ret |= regmap_write(ao_cec->regmap_cec, CECB_TX_CNT, msg->len); - if (ret) - return -EIO; - - ret = regmap_update_bits(ao_cec->regmap_cec, CECB_CTRL, - CECB_CTRL_SEND | - CECB_CTRL_TYPE, - CECB_CTRL_SEND | - FIELD_PREP(CECB_CTRL_TYPE, type)); - - return ret; -} - -static int meson_ao_cec_g12a_adap_enable(struct cec_adapter *adap, bool enable) -{ - struct meson_ao_cec_g12a_device *ao_cec = adap->priv; - - meson_ao_cec_g12a_irq_setup(ao_cec, false); - - regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG, - CECB_GEN_CNTL_RESET, CECB_GEN_CNTL_RESET); - - if (!enable) - return 0; - - /* Setup Filter */ - regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG, - CECB_GEN_CNTL_FILTER_TICK_SEL | - CECB_GEN_CNTL_FILTER_DEL, - FIELD_PREP(CECB_GEN_CNTL_FILTER_TICK_SEL, - CECB_GEN_CNTL_FILTER_TICK_1US) | - FIELD_PREP(CECB_GEN_CNTL_FILTER_DEL, 7)); - - /* Enable System Clock */ - regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG, - CECB_GEN_CNTL_SYS_CLK_EN, - CECB_GEN_CNTL_SYS_CLK_EN); - - /* Enable gated clock (Normal mode). */ - regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG, - CECB_GEN_CNTL_CLK_CTRL_MASK, - FIELD_PREP(CECB_GEN_CNTL_CLK_CTRL_MASK, - CECB_GEN_CNTL_CLK_ENABLE)); - - /* Release Reset */ - regmap_update_bits(ao_cec->regmap, CECB_GEN_CNTL_REG, - CECB_GEN_CNTL_RESET, 0); - - if (ao_cec->data->ctrl2_setup) - regmap_write(ao_cec->regmap_cec, CECB_CTRL2, - FIELD_PREP(CECB_CTRL2_RISE_DEL_MAX, 2)); - - meson_ao_cec_g12a_irq_setup(ao_cec, true); - - return 0; -} - -static const struct cec_adap_ops meson_ao_cec_g12a_ops = { - .adap_enable = meson_ao_cec_g12a_adap_enable, - .adap_log_addr = meson_ao_cec_g12a_set_log_addr, - .adap_transmit = meson_ao_cec_g12a_transmit, -}; - -static int meson_ao_cec_g12a_probe(struct platform_device *pdev) -{ - struct meson_ao_cec_g12a_device *ao_cec; - struct device *hdmi_dev; - struct resource *res; - void __iomem *base; - int ret, irq; - - hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev); - if (IS_ERR(hdmi_dev)) - return PTR_ERR(hdmi_dev); - - ao_cec = devm_kzalloc(&pdev->dev, sizeof(*ao_cec), GFP_KERNEL); - if (!ao_cec) - return -ENOMEM; - - ao_cec->data = of_device_get_match_data(&pdev->dev); - if (!ao_cec->data) { - dev_err(&pdev->dev, "failed to get match data\n"); - return -ENODEV; - } - - spin_lock_init(&ao_cec->cec_reg_lock); - ao_cec->pdev = pdev; - - ao_cec->adap = cec_allocate_adapter(&meson_ao_cec_g12a_ops, ao_cec, - "meson_g12a_ao_cec", - CEC_CAP_DEFAULTS | - CEC_CAP_CONNECTOR_INFO, - CEC_MAX_LOG_ADDRS); - if (IS_ERR(ao_cec->adap)) - return PTR_ERR(ao_cec->adap); - - ao_cec->adap->owner = THIS_MODULE; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) { - ret = PTR_ERR(base); - goto out_probe_adapter; - } - - ao_cec->regmap = devm_regmap_init_mmio(&pdev->dev, base, - &meson_ao_cec_g12a_regmap_conf); - if (IS_ERR(ao_cec->regmap)) { - ret = PTR_ERR(ao_cec->regmap); - goto out_probe_adapter; - } - - ao_cec->regmap_cec = devm_regmap_init(&pdev->dev, NULL, ao_cec, - &meson_ao_cec_g12a_cec_regmap_conf); - if (IS_ERR(ao_cec->regmap_cec)) { - ret = PTR_ERR(ao_cec->regmap_cec); - goto out_probe_adapter; - } - - irq = platform_get_irq(pdev, 0); - ret = devm_request_threaded_irq(&pdev->dev, irq, - meson_ao_cec_g12a_irq, - meson_ao_cec_g12a_irq_thread, - 0, NULL, ao_cec); - if (ret) { - dev_err(&pdev->dev, "irq request failed\n"); - goto out_probe_adapter; - } - - ao_cec->oscin = devm_clk_get(&pdev->dev, "oscin"); - if (IS_ERR(ao_cec->oscin)) { - dev_err(&pdev->dev, "oscin clock request failed\n"); - ret = PTR_ERR(ao_cec->oscin); - goto out_probe_adapter; - } - - ret = meson_ao_cec_g12a_setup_clk(ao_cec); - if (ret) - goto out_probe_adapter; - - ret = clk_prepare_enable(ao_cec->core); - if (ret) { - dev_err(&pdev->dev, "core clock enable failed\n"); - goto out_probe_adapter; - } - - device_reset_optional(&pdev->dev); - - platform_set_drvdata(pdev, ao_cec); - - ao_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, NULL, - ao_cec->adap); - if (!ao_cec->notify) { - ret = -ENOMEM; - goto out_probe_core_clk; - } - - ret = cec_register_adapter(ao_cec->adap, &pdev->dev); - if (ret < 0) - goto out_probe_notify; - - /* Setup Hardware */ - regmap_write(ao_cec->regmap, CECB_GEN_CNTL_REG, CECB_GEN_CNTL_RESET); - - return 0; - -out_probe_notify: - cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap); - -out_probe_core_clk: - clk_disable_unprepare(ao_cec->core); - -out_probe_adapter: - cec_delete_adapter(ao_cec->adap); - - dev_err(&pdev->dev, "CEC controller registration failed\n"); - - return ret; -} - -static int meson_ao_cec_g12a_remove(struct platform_device *pdev) -{ - struct meson_ao_cec_g12a_device *ao_cec = platform_get_drvdata(pdev); - - clk_disable_unprepare(ao_cec->core); - - cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap); - - cec_unregister_adapter(ao_cec->adap); - - return 0; -} - -static const struct meson_ao_cec_g12a_data ao_cec_g12a_data = { - .ctrl2_setup = false, -}; - -static const struct meson_ao_cec_g12a_data ao_cec_sm1_data = { - .ctrl2_setup = true, -}; - -static const struct of_device_id meson_ao_cec_g12a_of_match[] = { - { - .compatible = "amlogic,meson-g12a-ao-cec", - .data = &ao_cec_g12a_data, - }, - { - .compatible = "amlogic,meson-sm1-ao-cec", - .data = &ao_cec_sm1_data, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, meson_ao_cec_g12a_of_match); - -static struct platform_driver meson_ao_cec_g12a_driver = { - .probe = meson_ao_cec_g12a_probe, - .remove = meson_ao_cec_g12a_remove, - .driver = { - .name = "meson-ao-cec-g12a", - .of_match_table = of_match_ptr(meson_ao_cec_g12a_of_match), - }, -}; - -module_platform_driver(meson_ao_cec_g12a_driver); - -MODULE_DESCRIPTION("Meson AO CEC G12A Controller driver"); -MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/meson/ao-cec.c b/drivers/media/platform/meson/ao-cec.c deleted file mode 100644 index 09aff82c3773..000000000000 --- a/drivers/media/platform/meson/ao-cec.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Driver for Amlogic Meson AO CEC Controller - * - * Copyright (C) 2015 Amlogic, Inc. All rights reserved - * Copyright (C) 2017 BayLibre, SAS - * Author: Neil Armstrong <narmstrong@baylibre.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <linux/bitfield.h> -#include <linux/clk.h> -#include <linux/device.h> -#include <linux/io.h> -#include <linux/delay.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_platform.h> -#include <linux/platform_device.h> -#include <linux/types.h> -#include <linux/interrupt.h> -#include <linux/reset.h> -#include <media/cec.h> -#include <media/cec-notifier.h> - -/* CEC Registers */ - -/* - * [2:1] cntl_clk - * - 0 = Disable clk (Power-off mode) - * - 1 = Enable gated clock (Normal mode) - * - 2 = Enable free-run clk (Debug mode) - */ -#define CEC_GEN_CNTL_REG 0x00 - -#define CEC_GEN_CNTL_RESET BIT(0) -#define CEC_GEN_CNTL_CLK_DISABLE 0 -#define CEC_GEN_CNTL_CLK_ENABLE 1 -#define CEC_GEN_CNTL_CLK_ENABLE_DBG 2 -#define CEC_GEN_CNTL_CLK_CTRL_MASK GENMASK(2, 1) - -/* - * [7:0] cec_reg_addr - * [15:8] cec_reg_wrdata - * [16] cec_reg_wr - * - 0 = Read - * - 1 = Write - * [23] bus free - * [31:24] cec_reg_rddata - */ -#define CEC_RW_REG 0x04 - -#define CEC_RW_ADDR GENMASK(7, 0) -#define CEC_RW_WR_DATA GENMASK(15, 8) -#define CEC_RW_WRITE_EN BIT(16) -#define CEC_RW_BUS_BUSY BIT(23) -#define CEC_RW_RD_DATA GENMASK(31, 24) - -/* - * [1] tx intr - * [2] rx intr - */ -#define CEC_INTR_MASKN_REG 0x08 -#define CEC_INTR_CLR_REG 0x0c -#define CEC_INTR_STAT_REG 0x10 - -#define CEC_INTR_TX BIT(1) -#define CEC_INTR_RX BIT(2) - -/* CEC Commands */ - -#define CEC_TX_MSG_0_HEADER 0x00 -#define CEC_TX_MSG_1_OPCODE 0x01 -#define CEC_TX_MSG_2_OP1 0x02 -#define CEC_TX_MSG_3_OP2 0x03 -#define CEC_TX_MSG_4_OP3 0x04 -#define CEC_TX_MSG_5_OP4 0x05 -#define CEC_TX_MSG_6_OP5 0x06 -#define CEC_TX_MSG_7_OP6 0x07 -#define CEC_TX_MSG_8_OP7 0x08 -#define CEC_TX_MSG_9_OP8 0x09 -#define CEC_TX_MSG_A_OP9 0x0A -#define CEC_TX_MSG_B_OP10 0x0B -#define CEC_TX_MSG_C_OP11 0x0C -#define CEC_TX_MSG_D_OP12 0x0D -#define CEC_TX_MSG_E_OP13 0x0E -#define CEC_TX_MSG_F_OP14 0x0F -#define CEC_TX_MSG_LENGTH 0x10 -#define CEC_TX_MSG_CMD 0x11 -#define CEC_TX_WRITE_BUF 0x12 -#define CEC_TX_CLEAR_BUF 0x13 -#define CEC_RX_MSG_CMD 0x14 -#define CEC_RX_CLEAR_BUF 0x15 -#define CEC_LOGICAL_ADDR0 0x16 -#define CEC_LOGICAL_ADDR1 0x17 -#define CEC_LOGICAL_ADDR2 0x18 -#define CEC_LOGICAL_ADDR3 0x19 -#define CEC_LOGICAL_ADDR4 0x1A -#define CEC_CLOCK_DIV_H 0x1B -#define CEC_CLOCK_DIV_L 0x1C -#define CEC_QUIESCENT_25MS_BIT7_0 0x20 -#define CEC_QUIESCENT_25MS_BIT11_8 0x21 -#define CEC_STARTBITMINL2H_3MS5_BIT7_0 0x22 -#define CEC_STARTBITMINL2H_3MS5_BIT8 0x23 -#define CEC_STARTBITMAXL2H_3MS9_BIT7_0 0x24 -#define CEC_STARTBITMAXL2H_3MS9_BIT8 0x25 -#define CEC_STARTBITMINH_0MS6_BIT7_0 0x26 -#define CEC_STARTBITMINH_0MS6_BIT8 0x27 -#define CEC_STARTBITMAXH_1MS0_BIT7_0 0x28 -#define CEC_STARTBITMAXH_1MS0_BIT8 0x29 -#define CEC_STARTBITMINTOT_4MS3_BIT7_0 0x2A -#define CEC_STARTBITMINTOT_4MS3_BIT9_8 0x2B -#define CEC_STARTBITMAXTOT_4MS7_BIT7_0 0x2C -#define CEC_STARTBITMAXTOT_4MS7_BIT9_8 0x2D -#define CEC_LOGIC1MINL2H_0MS4_BIT7_0 0x2E -#define CEC_LOGIC1MINL2H_0MS4_BIT8 0x2F -#define CEC_LOGIC1MAXL2H_0MS8_BIT7_0 0x30 -#define CEC_LOGIC1MAXL2H_0MS8_BIT8 0x31 -#define CEC_LOGIC0MINL2H_1MS3_BIT7_0 0x32 -#define CEC_LOGIC0MINL2H_1MS3_BIT8 0x33 -#define CEC_LOGIC0MAXL2H_1MS7_BIT7_0 0x34 -#define CEC_LOGIC0MAXL2H_1MS7_BIT8 0x35 -#define CEC_LOGICMINTOTAL_2MS05_BIT7_0 0x36 -#define CEC_LOGICMINTOTAL_2MS05_BIT9_8 0x37 -#define CEC_LOGICMAXHIGH_2MS8_BIT7_0 0x38 -#define CEC_LOGICMAXHIGH_2MS8_BIT8 0x39 -#define CEC_LOGICERRLOW_3MS4_BIT7_0 0x3A -#define CEC_LOGICERRLOW_3MS4_BIT8 0x3B -#define CEC_NOMSMPPOINT_1MS05 0x3C -#define CEC_DELCNTR_LOGICERR 0x3E -#define CEC_TXTIME_17MS_BIT7_0 0x40 -#define CEC_TXTIME_17MS_BIT10_8 0x41 -#define CEC_TXTIME_2BIT_BIT7_0 0x42 -#define CEC_TXTIME_2BIT_BIT10_8 0x43 -#define CEC_TXTIME_4BIT_BIT7_0 0x44 -#define CEC_TXTIME_4BIT_BIT10_8 0x45 -#define CEC_STARTBITNOML2H_3MS7_BIT7_0 0x46 -#define CEC_STARTBITNOML2H_3MS7_BIT8 0x47 -#define CEC_STARTBITNOMH_0MS8_BIT7_0 0x48 -#define CEC_STARTBITNOMH_0MS8_BIT8 0x49 -#define CEC_LOGIC1NOML2H_0MS6_BIT7_0 0x4A -#define CEC_LOGIC1NOML2H_0MS6_BIT8 0x4B -#define CEC_LOGIC0NOML2H_1MS5_BIT7_0 0x4C -#define CEC_LOGIC0NOML2H_1MS5_BIT8 0x4D -#define CEC_LOGIC1NOMH_1MS8_BIT7_0 0x4E -#define CEC_LOGIC1NOMH_1MS8_BIT8 0x4F -#define CEC_LOGIC0NOMH_0MS9_BIT7_0 0x50 -#define CEC_LOGIC0NOMH_0MS9_BIT8 0x51 -#define CEC_LOGICERRLOW_3MS6_BIT7_0 0x52 -#define CEC_LOGICERRLOW_3MS6_BIT8 0x53 -#define CEC_CHKCONTENTION_0MS1 0x54 -#define CEC_PREPARENXTBIT_0MS05_BIT7_0 0x56 -#define CEC_PREPARENXTBIT_0MS05_BIT8 0x57 -#define CEC_NOMSMPACKPOINT_0MS45 0x58 -#define CEC_ACK0NOML2H_1MS5_BIT7_0 0x5A -#define CEC_ACK0NOML2H_1MS5_BIT8 0x5B -#define CEC_BUGFIX_DISABLE_0 0x60 -#define CEC_BUGFIX_DISABLE_1 0x61 -#define CEC_RX_MSG_0_HEADER 0x80 -#define CEC_RX_MSG_1_OPCODE 0x81 -#define CEC_RX_MSG_2_OP1 0x82 -#define CEC_RX_MSG_3_OP2 0x83 -#define CEC_RX_MSG_4_OP3 0x84 -#define CEC_RX_MSG_5_OP4 0x85 -#define CEC_RX_MSG_6_OP5 0x86 -#define CEC_RX_MSG_7_OP6 0x87 -#define CEC_RX_MSG_8_OP7 0x88 -#define CEC_RX_MSG_9_OP8 0x89 -#define CEC_RX_MSG_A_OP9 0x8A -#define CEC_RX_MSG_B_OP10 0x8B -#define CEC_RX_MSG_C_OP11 0x8C -#define CEC_RX_MSG_D_OP12 0x8D -#define CEC_RX_MSG_E_OP13 0x8E -#define CEC_RX_MSG_F_OP14 0x8F -#define CEC_RX_MSG_LENGTH 0x90 -#define CEC_RX_MSG_STATUS 0x91 -#define CEC_RX_NUM_MSG 0x92 -#define CEC_TX_MSG_STATUS 0x93 -#define CEC_TX_NUM_MSG 0x94 - - -/* CEC_TX_MSG_CMD definition */ -#define TX_NO_OP 0 /* No transaction */ -#define TX_REQ_CURRENT 1 /* Transmit earliest message in buffer */ -#define TX_ABORT 2 /* Abort transmitting earliest message */ -#define TX_REQ_NEXT 3 /* Overwrite earliest msg, transmit next */ - -/* tx_msg_status definition */ -#define TX_IDLE 0 /* No transaction */ -#define TX_BUSY 1 /* Transmitter is busy */ -#define TX_DONE 2 /* Message successfully transmitted */ -#define TX_ERROR 3 /* Message transmitted with error */ - -/* rx_msg_cmd */ -#define RX_NO_OP 0 /* No transaction */ -#define RX_ACK_CURRENT 1 /* Read earliest message in buffer */ -#define RX_DISABLE 2 /* Disable receiving latest message */ -#define RX_ACK_NEXT 3 /* Clear earliest msg, read next */ - -/* rx_msg_status */ -#define RX_IDLE 0 /* No transaction */ -#define RX_BUSY 1 /* Receiver is busy */ -#define RX_DONE 2 /* Message has been received successfully */ -#define RX_ERROR 3 /* Message has been received with error */ - -/* RX_CLEAR_BUF options */ -#define CLEAR_START 1 -#define CLEAR_STOP 0 - -/* CEC_LOGICAL_ADDRx options */ -#define LOGICAL_ADDR_MASK 0xf -#define LOGICAL_ADDR_VALID BIT(4) -#define LOGICAL_ADDR_DISABLE 0 - -#define CEC_CLK_RATE 32768 - -struct meson_ao_cec_device { - struct platform_device *pdev; - void __iomem *base; - struct clk *core; - spinlock_t cec_reg_lock; - struct cec_notifier *notify; - struct cec_adapter *adap; - struct cec_msg rx_msg; -}; - -#define writel_bits_relaxed(mask, val, addr) \ - writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr) - -static inline int meson_ao_cec_wait_busy(struct meson_ao_cec_device *ao_cec) -{ - ktime_t timeout = ktime_add_us(ktime_get(), 5000); - - while (readl_relaxed(ao_cec->base + CEC_RW_REG) & CEC_RW_BUS_BUSY) { - if (ktime_compare(ktime_get(), timeout) > 0) - return -ETIMEDOUT; - } - - return 0; -} - -static void meson_ao_cec_read(struct meson_ao_cec_device *ao_cec, - unsigned long address, u8 *data, - int *res) -{ - unsigned long flags; - u32 reg = FIELD_PREP(CEC_RW_ADDR, address); - int ret = 0; - - if (res && *res) - return; - - spin_lock_irqsave(&ao_cec->cec_reg_lock, flags); - - ret = meson_ao_cec_wait_busy(ao_cec); - if (ret) - goto read_out; - - writel_relaxed(reg, ao_cec->base + CEC_RW_REG); - - ret = meson_ao_cec_wait_busy(ao_cec); - if (ret) - goto read_out; - - *data = FIELD_GET(CEC_RW_RD_DATA, - readl_relaxed(ao_cec->base + CEC_RW_REG)); - -read_out: - spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags); - - if (res) - *res = ret; -} - -static void meson_ao_cec_write(struct meson_ao_cec_device *ao_cec, - unsigned long address, u8 data, - int *res) -{ - unsigned long flags; - u32 reg = FIELD_PREP(CEC_RW_ADDR, address) | - FIELD_PREP(CEC_RW_WR_DATA, data) | - CEC_RW_WRITE_EN; - int ret = 0; - - if (res && *res) - return; - - spin_lock_irqsave(&ao_cec->cec_reg_lock, flags); - - ret = meson_ao_cec_wait_busy(ao_cec); - if (ret) - goto write_out; - - writel_relaxed(reg, ao_cec->base + CEC_RW_REG); - -write_out: - spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags); - - if (res) - *res = ret; -} - -static inline void meson_ao_cec_irq_setup(struct meson_ao_cec_device *ao_cec, - bool enable) -{ - u32 cfg = CEC_INTR_TX | CEC_INTR_RX; - - writel_bits_relaxed(cfg, enable ? cfg : 0, - ao_cec->base + CEC_INTR_MASKN_REG); -} - -static inline int meson_ao_cec_clear(struct meson_ao_cec_device *ao_cec) -{ - int ret = 0; - - meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_DISABLE, &ret); - meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret); - meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 1, &ret); - meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 1, &ret); - if (ret) - return ret; - - udelay(100); - - meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 0, &ret); - meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 0, &ret); - if (ret) - return ret; - - udelay(100); - - meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP, &ret); - meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP, &ret); - - return ret; -} - -static int meson_ao_cec_arbit_bit_time_set(struct meson_ao_cec_device *ao_cec, - unsigned int bit_set, - unsigned int time_set) -{ - int ret = 0; - - switch (bit_set) { - case CEC_SIGNAL_FREE_TIME_RETRY: - meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT7_0, - time_set & 0xff, &ret); - meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT10_8, - (time_set >> 8) & 0x7, &ret); - break; - - case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR: - meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT7_0, - time_set & 0xff, &ret); - meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT10_8, - (time_set >> 8) & 0x7, &ret); - break; - - case CEC_SIGNAL_FREE_TIME_NEXT_XFER: - meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT7_0, - time_set & 0xff, &ret); - meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT10_8, - (time_set >> 8) & 0x7, &ret); - break; - } - - return ret; -} - -static irqreturn_t meson_ao_cec_irq(int irq, void *data) -{ - struct meson_ao_cec_device *ao_cec = data; - u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG); - - if (stat) - return IRQ_WAKE_THREAD; - - return IRQ_NONE; -} - -static void meson_ao_cec_irq_tx(struct meson_ao_cec_device *ao_cec) -{ - unsigned long tx_status = 0; - u8 stat; - int ret = 0; - - meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS, &stat, &ret); - if (ret) - goto tx_reg_err; - - switch (stat) { - case TX_DONE: - tx_status = CEC_TX_STATUS_OK; - break; - - case TX_BUSY: - tx_status = CEC_TX_STATUS_ARB_LOST; - break; - - case TX_IDLE: - tx_status = CEC_TX_STATUS_LOW_DRIVE; - break; - - case TX_ERROR: - default: - tx_status = CEC_TX_STATUS_NACK; - break; - } - - /* Clear Interruption */ - writel_relaxed(CEC_INTR_TX, ao_cec->base + CEC_INTR_CLR_REG); - - /* Stop TX */ - meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP, &ret); - if (ret) - goto tx_reg_err; - - cec_transmit_attempt_done(ao_cec->adap, tx_status); - return; - -tx_reg_err: - cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ERROR); -} - -static void meson_ao_cec_irq_rx(struct meson_ao_cec_device *ao_cec) -{ - int i, ret = 0; - u8 reg; - - meson_ao_cec_read(ao_cec, CEC_RX_MSG_STATUS, ®, &ret); - if (reg != RX_DONE) - goto rx_out; - - meson_ao_cec_read(ao_cec, CEC_RX_NUM_MSG, ®, &ret); - if (reg != 1) - goto rx_out; - - meson_ao_cec_read(ao_cec, CEC_RX_MSG_LENGTH, ®, &ret); - - ao_cec->rx_msg.len = reg + 1; - if (ao_cec->rx_msg.len > CEC_MAX_MSG_SIZE) - ao_cec->rx_msg.len = CEC_MAX_MSG_SIZE; - - for (i = 0; i < ao_cec->rx_msg.len; i++) { - u8 byte; - - meson_ao_cec_read(ao_cec, CEC_RX_MSG_0_HEADER + i, &byte, &ret); - - ao_cec->rx_msg.msg[i] = byte; - } - - if (ret) - goto rx_out; - - cec_received_msg(ao_cec->adap, &ao_cec->rx_msg); - -rx_out: - /* Clear Interruption */ - writel_relaxed(CEC_INTR_RX, ao_cec->base + CEC_INTR_CLR_REG); - - /* Ack RX message */ - meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_ACK_CURRENT, &ret); - meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP, &ret); - - /* Clear RX buffer */ - meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_START, &ret); - meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_STOP, &ret); -} - -static irqreturn_t meson_ao_cec_irq_thread(int irq, void *data) -{ - struct meson_ao_cec_device *ao_cec = data; - u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG); - - if (stat & CEC_INTR_TX) - meson_ao_cec_irq_tx(ao_cec); - - meson_ao_cec_irq_rx(ao_cec); - - return IRQ_HANDLED; -} - -static int meson_ao_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr) -{ - struct meson_ao_cec_device *ao_cec = adap->priv; - int ret = 0; - - meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0, - LOGICAL_ADDR_DISABLE, &ret); - if (ret) - return ret; - - ret = meson_ao_cec_clear(ao_cec); - if (ret) - return ret; - - if (logical_addr == CEC_LOG_ADDR_INVALID) - return 0; - - meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0, - logical_addr & LOGICAL_ADDR_MASK, &ret); - if (ret) - return ret; - - udelay(100); - - meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0, - (logical_addr & LOGICAL_ADDR_MASK) | - LOGICAL_ADDR_VALID, &ret); - - return ret; -} - -static int meson_ao_cec_transmit(struct cec_adapter *adap, u8 attempts, - u32 signal_free_time, struct cec_msg *msg) -{ - struct meson_ao_cec_device *ao_cec = adap->priv; - int i, ret = 0; - u8 reg; - - meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS, ®, &ret); - if (ret) - return ret; - - if (reg == TX_BUSY) { - dev_dbg(&ao_cec->pdev->dev, "%s: busy TX: aborting\n", - __func__); - meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret); - } - - for (i = 0; i < msg->len; i++) { - meson_ao_cec_write(ao_cec, CEC_TX_MSG_0_HEADER + i, - msg->msg[i], &ret); - } - - meson_ao_cec_write(ao_cec, CEC_TX_MSG_LENGTH, msg->len - 1, &ret); - meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_REQ_CURRENT, &ret); - - return ret; -} - -static int meson_ao_cec_adap_enable(struct cec_adapter *adap, bool enable) -{ - struct meson_ao_cec_device *ao_cec = adap->priv; - int ret; - - meson_ao_cec_irq_setup(ao_cec, false); - - writel_bits_relaxed(CEC_GEN_CNTL_RESET, CEC_GEN_CNTL_RESET, - ao_cec->base + CEC_GEN_CNTL_REG); - - if (!enable) - return 0; - - /* Enable gated clock (Normal mode). */ - writel_bits_relaxed(CEC_GEN_CNTL_CLK_CTRL_MASK, - FIELD_PREP(CEC_GEN_CNTL_CLK_CTRL_MASK, - CEC_GEN_CNTL_CLK_ENABLE), - ao_cec->base + CEC_GEN_CNTL_REG); - - udelay(100); - - /* Release Reset */ - writel_bits_relaxed(CEC_GEN_CNTL_RESET, 0, - ao_cec->base + CEC_GEN_CNTL_REG); - - /* Clear buffers */ - ret = meson_ao_cec_clear(ao_cec); - if (ret) - return ret; - - /* CEC arbitration 3/5/7 bit time set. */ - ret = meson_ao_cec_arbit_bit_time_set(ao_cec, - CEC_SIGNAL_FREE_TIME_RETRY, - 0x118); - if (ret) - return ret; - ret = meson_ao_cec_arbit_bit_time_set(ao_cec, - CEC_SIGNAL_FREE_TIME_NEW_INITIATOR, - 0x000); - if (ret) - return ret; - ret = meson_ao_cec_arbit_bit_time_set(ao_cec, - CEC_SIGNAL_FREE_TIME_NEXT_XFER, - 0x2aa); - if (ret) - return ret; - - meson_ao_cec_irq_setup(ao_cec, true); - - return 0; -} - -static const struct cec_adap_ops meson_ao_cec_ops = { - .adap_enable = meson_ao_cec_adap_enable, - .adap_log_addr = meson_ao_cec_set_log_addr, - .adap_transmit = meson_ao_cec_transmit, -}; - -static int meson_ao_cec_probe(struct platform_device *pdev) -{ - struct meson_ao_cec_device *ao_cec; - struct device *hdmi_dev; - struct resource *res; - int ret, irq; - - hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev); - - if (IS_ERR(hdmi_dev)) - return PTR_ERR(hdmi_dev); - - ao_cec = devm_kzalloc(&pdev->dev, sizeof(*ao_cec), GFP_KERNEL); - if (!ao_cec) - return -ENOMEM; - - spin_lock_init(&ao_cec->cec_reg_lock); - - ao_cec->adap = cec_allocate_adapter(&meson_ao_cec_ops, ao_cec, - "meson_ao_cec", - CEC_CAP_DEFAULTS | - CEC_CAP_CONNECTOR_INFO, - 1); /* Use 1 for now */ - if (IS_ERR(ao_cec->adap)) - return PTR_ERR(ao_cec->adap); - - ao_cec->adap->owner = THIS_MODULE; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ao_cec->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(ao_cec->base)) { - ret = PTR_ERR(ao_cec->base); - goto out_probe_adapter; - } - - irq = platform_get_irq(pdev, 0); - ret = devm_request_threaded_irq(&pdev->dev, irq, - meson_ao_cec_irq, - meson_ao_cec_irq_thread, - 0, NULL, ao_cec); - if (ret) { - dev_err(&pdev->dev, "irq request failed\n"); - goto out_probe_adapter; - } - - ao_cec->core = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(ao_cec->core)) { - dev_err(&pdev->dev, "core clock request failed\n"); - ret = PTR_ERR(ao_cec->core); - goto out_probe_adapter; - } - - ret = clk_prepare_enable(ao_cec->core); - if (ret) { - dev_err(&pdev->dev, "core clock enable failed\n"); - goto out_probe_adapter; - } - - ret = clk_set_rate(ao_cec->core, CEC_CLK_RATE); - if (ret) { - dev_err(&pdev->dev, "core clock set rate failed\n"); - goto out_probe_clk; - } - - device_reset_optional(&pdev->dev); - - ao_cec->pdev = pdev; - platform_set_drvdata(pdev, ao_cec); - - ao_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, NULL, - ao_cec->adap); - if (!ao_cec->notify) { - ret = -ENOMEM; - goto out_probe_clk; - } - - ret = cec_register_adapter(ao_cec->adap, &pdev->dev); - if (ret < 0) - goto out_probe_notify; - - /* Setup Hardware */ - writel_relaxed(CEC_GEN_CNTL_RESET, - ao_cec->base + CEC_GEN_CNTL_REG); - - return 0; - -out_probe_notify: - cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap); - -out_probe_clk: - clk_disable_unprepare(ao_cec->core); - -out_probe_adapter: - cec_delete_adapter(ao_cec->adap); - - dev_err(&pdev->dev, "CEC controller registration failed\n"); - - return ret; -} - -static int meson_ao_cec_remove(struct platform_device *pdev) -{ - struct meson_ao_cec_device *ao_cec = platform_get_drvdata(pdev); - - clk_disable_unprepare(ao_cec->core); - - cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap); - cec_unregister_adapter(ao_cec->adap); - - return 0; -} - -static const struct of_device_id meson_ao_cec_of_match[] = { - { .compatible = "amlogic,meson-gx-ao-cec", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, meson_ao_cec_of_match); - -static struct platform_driver meson_ao_cec_driver = { - .probe = meson_ao_cec_probe, - .remove = meson_ao_cec_remove, - .driver = { - .name = "meson-ao-cec", - .of_match_table = of_match_ptr(meson_ao_cec_of_match), - }, -}; - -module_platform_driver(meson_ao_cec_driver); - -MODULE_DESCRIPTION("Meson AO CEC Controller driver"); -MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); -MODULE_LICENSE("GPL"); |