From 4feec5ce1611313210c2809283b6fa30e26c2a26 Mon Sep 17 00:00:00 2001 From: "Mauro (mdrjr) Ribeiro" Date: Fri, 24 Feb 2017 11:47:19 +0900 Subject: [PATCH] s5p-cec: Add legacy s5p-cec driver This driver supports Pulse8 libcec library. Change-Id: Ic6189187ec930a59ffac024a5c5436270f7e83a6 --- arch/arm/boot/dts/exynos5420-pinctrl.dtsi | 7 + .../boot/dts/exynos5422-odroidxu3-common.dtsi | 23 + arch/arm/configs/odroidxu3_defconfig | 6 +- drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 2 +- drivers/media/platform/s5p-cec/Kconfig | 6 + drivers/media/platform/s5p-cec/Makefile | 2 + drivers/media/platform/s5p-cec/cec.h | 86 ++++ drivers/media/platform/s5p-cec/hdmi_cec.c | 421 ++++++++++++++++++ .../media/platform/s5p-cec/hdmi_cec_ctrl.c | 247 ++++++++++ drivers/media/platform/s5p-cec/regs-cec.h | 93 ++++ 11 files changed, 891 insertions(+), 3 deletions(-) create mode 100644 drivers/media/platform/s5p-cec/Kconfig create mode 100644 drivers/media/platform/s5p-cec/Makefile create mode 100644 drivers/media/platform/s5p-cec/cec.h create mode 100644 drivers/media/platform/s5p-cec/hdmi_cec.c create mode 100644 drivers/media/platform/s5p-cec/hdmi_cec_ctrl.c create mode 100644 drivers/media/platform/s5p-cec/regs-cec.h diff --git a/arch/arm/boot/dts/exynos5420-pinctrl.dtsi b/arch/arm/boot/dts/exynos5420-pinctrl.dtsi index 3924b4fafe72..62abbbd7443d 100644 --- a/arch/arm/boot/dts/exynos5420-pinctrl.dtsi +++ b/arch/arm/boot/dts/exynos5420-pinctrl.dtsi @@ -67,6 +67,13 @@ samsung,pin-pud = ; samsung,pin-drv = ; }; + + hdmi_cec: hdmi_cec { + samsung,pins = "gpx3-6"; + samsung,pin-function = ; + samsung,pin-pud = ; + samsung,pin-drv = ; + }; }; &pinctrl_1 { diff --git a/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi b/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi index 45bf48d2c137..7341aa68f78f 100755 --- a/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi +++ b/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi @@ -81,6 +81,29 @@ status = "disabled"; }; + + cec: cec@101B0000 { + compatible = "samsung,exynos5-cec"; + reg = <0x101B0000 0x1000>; + interrupts = <0 114 0>; + + gpios = <&gpx3 6 0xf>; + pinctrl-names = "hdmi_cec"; + pinctrl-0 = <&hdmi_cec>; + + clock-names = "hdmicec"; + clocks = <&clock CLK_HDMI_CEC>; + + #address-cells = <1>; + #size-cells = <1>; + ranges; + + status = "okay"; + + hdmiphy-sys { + reg = <0x10040700 0x4>; + }; + }; thermal-zones { cpu0_thermal: cpu0-thermal { diff --git a/arch/arm/configs/odroidxu3_defconfig b/arch/arm/configs/odroidxu3_defconfig index 6a5c7ad26e1c..2216319527cc 100644 --- a/arch/arm/configs/odroidxu3_defconfig +++ b/arch/arm/configs/odroidxu3_defconfig @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 4.9.12 Kernel Configuration +# Linux/arm 4.9.13 Kernel Configuration # CONFIG_ARM=y CONFIG_ARM_HAS_SG_CHAIN=y @@ -3238,7 +3238,9 @@ CONFIG_V4L_TEST_DRIVERS=y CONFIG_VIDEO_VIVID=m CONFIG_VIDEO_VIVID_MAX_DEVS=64 # CONFIG_VIDEO_VIM2M is not set -# CONFIG_DVB_PLATFORM_DRIVERS is not set +CONFIG_DVB_PLATFORM_DRIVERS=y +# CONFIG_DVB_C8SECTPFE is not set +CONFIG_EXYNOS_HDMI_CEC=y # # Supported MMC/SDIO adapters diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 5ff803efdc03..393b17ef4407 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -375,4 +375,5 @@ menuconfig DVB_PLATFORM_DRIVERS if DVB_PLATFORM_DRIVERS source "drivers/media/platform/sti/c8sectpfe/Kconfig" +source "drivers/media/platform/s5p-cec/Kconfig" endif #DVB_PLATFORM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 40b18d12726e..818b1009b328 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -51,7 +51,7 @@ obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ -obj-y += omap/ +obj-y += omap/ s5p-cec/ obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ diff --git a/drivers/media/platform/s5p-cec/Kconfig b/drivers/media/platform/s5p-cec/Kconfig new file mode 100644 index 000000000000..07b34c0da15e --- /dev/null +++ b/drivers/media/platform/s5p-cec/Kconfig @@ -0,0 +1,6 @@ +config EXYNOS_HDMI_CEC + tristate "Samsung HDMI CEC Driver" + help + Say Y here if you want support for the HDMI CEC + interface in S5P Samsung SoC. The driver can be compiled + as module. diff --git a/drivers/media/platform/s5p-cec/Makefile b/drivers/media/platform/s5p-cec/Makefile new file mode 100644 index 000000000000..2fafbe203286 --- /dev/null +++ b/drivers/media/platform/s5p-cec/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_EXYNOS_HDMI_CEC) += s5p-hdmi_cec.o +s5p-hdmi_cec-y += hdmi_cec.o hdmi_cec_ctrl.o diff --git a/drivers/media/platform/s5p-cec/cec.h b/drivers/media/platform/s5p-cec/cec.h new file mode 100644 index 000000000000..9aa6bb7b6deb --- /dev/null +++ b/drivers/media/platform/s5p-cec/cec.h @@ -0,0 +1,86 @@ +/* linux/drivers/media/video/samsung/tvout/hw_if/hw_if.h + * + * Copyright (c) 2010 Samsung Electronics + * http://www.samsung.com/ + * + * Header file for interface of Samsung TVOUT-related hardware + * + * 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. + */ + +#ifndef _SAMSUNG_TVOUT_CEC_H_ +#define _SAMSUNG_TVOUT_CEC_H_ __FILE__ + +/***************************************************************************** + * This file includes declarations for external functions of + * Samsung TVOUT-related hardware. So only external functions + * to be used by higher layer must exist in this file. + * + * Higher layer must use only the declarations included in this file. + ****************************************************************************/ + +#define to_tvout_plat(d) (to_platform_device(d)->dev.platform_data) + +#define DRV_NAME "s5p-cec" + +#ifndef tvout_dbg +#ifdef CONFIG_TV_DEBUG +#define tvout_dbg(fmt, ...) \ + pr_emerg("[%s] %s(): " fmt, \ + DRV_NAME, __func__, ##__VA_ARGS__) +#else +#define tvout_dbg(fmt, ...) +#endif +#endif + +enum s5p_tvout_endian { + TVOUT_LITTLE_ENDIAN = 0, + TVOUT_BIG_ENDIAN = 1 +}; + +enum cec_state { + STATE_RX, + STATE_TX, + STATE_DONE, + STATE_ERROR +}; + +struct cec_rx_struct { + spinlock_t lock; + wait_queue_head_t waitq; + atomic_t state; + u8 *buffer; + unsigned int size; +}; + +struct cec_tx_struct { + wait_queue_head_t waitq; + atomic_t state; +}; + +extern struct cec_rx_struct cec_rx_struct; +extern struct cec_tx_struct cec_tx_struct; + +void s5p_cec_set_divider(void); +void s5p_cec_enable_rx(void); +void s5p_cec_mask_rx_interrupts(void); +void s5p_cec_unmask_rx_interrupts(void); +void s5p_cec_mask_tx_interrupts(void); +void s5p_cec_unmask_tx_interrupts(void); +void s5p_cec_reset(void); +void s5p_cec_tx_reset(void); +void s5p_cec_rx_reset(void); +void s5p_cec_threshold(void); +void s5p_cec_set_tx_state(enum cec_state state); +void s5p_cec_set_rx_state(enum cec_state state); +void s5p_cec_copy_packet(char *data, size_t count); +void s5p_cec_set_addr(u32 addr); +u32 s5p_cec_get_status(void); +void s5p_clr_pending_tx(void); +void s5p_clr_pending_rx(void); +void s5p_cec_get_rx_buf(u32 size, u8 *buffer); +int s5p_cec_mem_probe(struct platform_device *pdev); + +#endif /* _SAMSUNG_TVOUT_CEC_H_ */ diff --git a/drivers/media/platform/s5p-cec/hdmi_cec.c b/drivers/media/platform/s5p-cec/hdmi_cec.c new file mode 100644 index 000000000000..567a235dbde2 --- /dev/null +++ b/drivers/media/platform/s5p-cec/hdmi_cec.c @@ -0,0 +1,421 @@ +/* linux/drivers/media/video/samsung/tvout/s5p_cec_ctrl.c + * + * Copyright (c) 2009 Samsung Electronics + * http://www.samsung.com/ + * + * cec interface file for Samsung TVOut driver + * + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cec.h" + +static const struct of_device_id cec_device_table[] = { + { + .compatible = "samsung,exynos5-cec", + .data = (void *)0, + }, + { /* empty */ } +}; +MODULE_DEVICE_TABLE(of, cec_device_table); + +MODULE_AUTHOR("SangPil Moon"); +MODULE_DESCRIPTION("S5P CEC driver"); +MODULE_LICENSE("GPL"); + +#define CEC_IOC_MAGIC 'c' +#define CEC_IOC_SETLADDR _IOW(CEC_IOC_MAGIC, 0, unsigned int) + +#define VERSION "1.0" /* Driver version number */ +#define CEC_MINOR 243 /* Major 10, Minor 242, /dev/cec */ +#define TVOUT_TIMEOUT (3000) + +#define CEC_STATUS_TX_RUNNING (1<<0) +#define CEC_STATUS_TX_TRANSFERRING (1<<1) +#define CEC_STATUS_TX_DONE (1<<2) +#define CEC_STATUS_TX_ERROR (1<<3) +#define CEC_STATUS_TX_BYTES (0xFF<<8) +#define CEC_STATUS_RX_RUNNING (1<<16) +#define CEC_STATUS_RX_RECEIVING (1<<17) +#define CEC_STATUS_RX_DONE (1<<18) +#define CEC_STATUS_RX_ERROR (1<<19) +#define CEC_STATUS_RX_BCAST (1<<20) +#define CEC_STATUS_RX_BYTES (0xFF<<24) + + +/* CEC Rx buffer size */ +#define CEC_RX_BUFF_SIZE 16 +/* CEC Tx buffer size */ +#define CEC_TX_BUFF_SIZE 16 + +#define TV_CLK_GET_WITH_ERR_CHECK(clk, pdev, clk_name) \ + do { \ + clk = clk_get(&pdev->dev, clk_name); \ + if (IS_ERR(clk)) { \ + tvout_dbg( \ + "failed to find clock %s\n", clk_name); \ + return -ENOENT; \ + } \ + } while (0); + +static atomic_t hdmi_on = ATOMIC_INIT(0); +static DEFINE_MUTEX(cec_lock); +struct clk *hdmi_cec_clk; + +static int s5p_cec_open(struct inode *inode, struct file *file) +{ + int ret = 0; + + mutex_lock(&cec_lock); + clk_prepare_enable(hdmi_cec_clk); + + if (atomic_read(&hdmi_on)) { + tvout_dbg("do not allow multiple open for tvout cec\n"); + ret = -EBUSY; + goto err_multi_open; + } else + atomic_inc(&hdmi_on); + + s5p_cec_reset(); + + s5p_cec_set_divider(); + + s5p_cec_threshold(); + + s5p_cec_unmask_tx_interrupts(); + + s5p_cec_set_rx_state(STATE_RX); + s5p_cec_unmask_rx_interrupts(); + s5p_cec_enable_rx(); + +err_multi_open: + mutex_unlock(&cec_lock); + + return ret; +} + +static int s5p_cec_release(struct inode *inode, struct file *file) +{ + atomic_dec(&hdmi_on); + + s5p_cec_mask_tx_interrupts(); + s5p_cec_mask_rx_interrupts(); + + clk_disable_unprepare(hdmi_cec_clk); + clk_put(hdmi_cec_clk); + + return 0; +} + +static ssize_t s5p_cec_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + ssize_t retval; + unsigned long spin_flags; + + if (wait_event_interruptible_timeout(cec_tx_struct.waitq, + atomic_read(&cec_rx_struct.state) == STATE_DONE, + msecs_to_jiffies(TVOUT_TIMEOUT)) == 0) { + tvout_dbg("error : waiting for interrupt is timeout\n"); + return -ERESTARTSYS; + } + spin_lock_irqsave(&cec_rx_struct.lock, spin_flags); + + if (cec_rx_struct.size > count) { + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + return -1; + } + + if (copy_to_user(buffer, cec_rx_struct.buffer, cec_rx_struct.size)) { + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + tvout_dbg(" copy_to_user() failed!\n"); + + return -EFAULT; + } + + retval = cec_rx_struct.size; + + s5p_cec_set_rx_state(STATE_RX); + spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags); + + return retval; +} + +static ssize_t s5p_cec_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char *data; + + /* check data size */ + + if (count > CEC_TX_BUFF_SIZE || count == 0) + return -1; + + data = kmalloc(count, GFP_KERNEL); + + if (!data) { + tvout_dbg(" kmalloc() failed!\n"); + + return -1; + } + + if (copy_from_user(data, buffer, count)) { + tvout_dbg(" copy_from_user() failed!\n"); + kfree(data); + + return -EFAULT; + } + + s5p_cec_copy_packet(data, count); + + kfree(data); + + /* wait for interrupt */ + if (wait_event_interruptible_timeout(cec_tx_struct.waitq, + atomic_read(&cec_tx_struct.state) != STATE_TX, + msecs_to_jiffies(TVOUT_TIMEOUT)) == 0) { + tvout_dbg("error : waiting for interrupt is timeout\n"); + return -ERESTARTSYS; + } + + if (atomic_read(&cec_tx_struct.state) == STATE_ERROR) + return -1; + + return count; +} + +#if 0 +static int s5p_cec_ioctl(struct inode *inode, struct file *file, u32 cmd, + unsigned long arg) +#else +static long s5p_cec_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +#endif +{ + u32 laddr; + + switch (cmd) { + case CEC_IOC_SETLADDR: + if (get_user(laddr, (u32 __user *) arg)) + return -EFAULT; + + tvout_dbg("logical address = 0x%02x\n", laddr); + + s5p_cec_set_addr(laddr); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static u32 s5p_cec_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &cec_rx_struct.waitq, wait); + + if (atomic_read(&cec_rx_struct.state) == STATE_DONE) + return POLLIN | POLLRDNORM; + + return 0; +} + +static const struct file_operations cec_fops = { + .owner = THIS_MODULE, + .open = s5p_cec_open, + .release = s5p_cec_release, + .read = s5p_cec_read, + .write = s5p_cec_write, +#if 1 + .unlocked_ioctl = s5p_cec_ioctl, +#else + .ioctl = s5p_cec_ioctl, +#endif + .poll = s5p_cec_poll, +}; + +static struct miscdevice cec_misc_device = { + .minor = CEC_MINOR, + .name = "CEC", + .fops = &cec_fops, +}; + +static irqreturn_t s5p_cec_irq_handler(int irq, void *dev_id) +{ + + u32 status = 0; + + status = s5p_cec_get_status(); + + if (status & CEC_STATUS_TX_DONE) { + if (status & CEC_STATUS_TX_ERROR) { + tvout_dbg(" CEC_STATUS_TX_ERROR!\n"); + s5p_cec_set_tx_state(STATE_ERROR); + } else { + tvout_dbg(" CEC_STATUS_TX_DONE!\n"); + s5p_cec_set_tx_state(STATE_DONE); + } + + s5p_clr_pending_tx(); + + wake_up_interruptible(&cec_tx_struct.waitq); + } + + if (status & CEC_STATUS_RX_DONE) { + if (status & CEC_STATUS_RX_ERROR) { + tvout_dbg(" CEC_STATUS_RX_ERROR!\n"); + s5p_cec_rx_reset(); + s5p_cec_enable_rx(); + } else { + u32 size; + + tvout_dbg(" CEC_STATUS_RX_DONE!\n"); + + /* copy data from internal buffer */ + size = status >> 24; + + spin_lock(&cec_rx_struct.lock); + + s5p_cec_get_rx_buf(size, cec_rx_struct.buffer); + + cec_rx_struct.size = size; + + s5p_cec_set_rx_state(STATE_DONE); + + spin_unlock(&cec_rx_struct.lock); + + s5p_cec_enable_rx(); + } + + /* clear interrupt pending bit */ + s5p_clr_pending_rx(); + + wake_up_interruptible(&cec_rx_struct.waitq); + } + + return IRQ_HANDLED; +} + +static int s5p_cec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct pinctrl *pinctrl; + int gpio; + int ret; + u8 *buffer; + + s5p_cec_mem_probe(pdev); + + if (misc_register(&cec_misc_device)) { + tvout_dbg(" Couldn't register device 10, %d.\n", + CEC_MINOR); + return -EBUSY; + } + + if (of_get_property(dev->of_node, "gpios", NULL) != NULL) { + gpio = of_get_gpio(dev->of_node, 0); + if (gpio < 0) { + tvout_dbg("failed to get gpio cec\n"); + return -ENODEV; + } + if (gpio_request(gpio, "hdmi-cec")) { + tvout_dbg("failed to request cec gpio\n"); + return -ENODEV; + } else { + gpio_direction_input(gpio); + pinctrl = devm_pinctrl_get_select(dev, "hdmi_cec"); + if (IS_ERR(pinctrl)) + tvout_dbg("failed to set cec gpio"); + tvout_dbg("success request GPIO for hdmi-cec"); + } + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + tvout_dbg("failed to get irq resource.\n"); + return -ENXIO; + } + ret = devm_request_irq(dev, res->start, s5p_cec_irq_handler, + 0x00000020, "hdmi-cec", &pdev->id); + if (ret) { + tvout_dbg("request int interrupt failed.\n"); + return ret; + } + + init_waitqueue_head(&cec_rx_struct.waitq); + spin_lock_init(&cec_rx_struct.lock); + init_waitqueue_head(&cec_tx_struct.waitq); + + buffer = kmalloc(CEC_TX_BUFF_SIZE, GFP_KERNEL); + + if (!buffer) { + tvout_dbg(" kmalloc() failed!\n"); + misc_deregister(&cec_misc_device); + + return -EIO; + } + + cec_rx_struct.buffer = buffer; + + cec_rx_struct.size = 0; + TV_CLK_GET_WITH_ERR_CHECK(hdmi_cec_clk, pdev, "hdmicec"); + + tvout_dbg("probe successful\n"); + + return 0; +} + +static int s5p_cec_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver s5p_cec_driver = { + .probe = s5p_cec_probe, + .remove = s5p_cec_remove, + .driver = { + .name = "s5p-tvout-cec", + .of_match_table = cec_device_table, + } +}; + +static int __init s5p_cec_init(void) +{ + int ret; + + ret = platform_driver_register(&s5p_cec_driver); + if (ret) { + pr_emerg("Platform Device Register Failed %d\n", ret); + + return -1; + } + + return 0; +} + +static void __exit s5p_cec_exit(void) +{ + kfree(cec_rx_struct.buffer); + + platform_driver_unregister(&s5p_cec_driver); +} + +module_init(s5p_cec_init); +module_exit(s5p_cec_exit); diff --git a/drivers/media/platform/s5p-cec/hdmi_cec_ctrl.c b/drivers/media/platform/s5p-cec/hdmi_cec_ctrl.c new file mode 100644 index 000000000000..0877270677c9 --- /dev/null +++ b/drivers/media/platform/s5p-cec/hdmi_cec_ctrl.c @@ -0,0 +1,247 @@ +/* linux/drivers/media/video/samsung/tvout/hw_if/cec.c + * + * Copyright (c) 2009 Samsung Electronics + * http://www.samsung.com/ + * + * cec ftn file for Samsung TVOUT driver + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "regs-cec.h" +#include "cec.h" + +#define S5P_HDMI_FIN 24000000 +#define CEC_DIV_RATIO 320000 + +#define CEC_MESSAGE_BROADCAST_MASK 0x0F +#define CEC_MESSAGE_BROADCAST 0x0F +#define CEC_FILTER_THRESHOLD 0x15 + +void __iomem *cec_base; +void __iomem *pmu_regs; + +struct cec_rx_struct cec_rx_struct; +struct cec_tx_struct cec_tx_struct; + +void s5p_cec_set_divider(void) +{ + u32 div_ratio, reg, div_val; + + div_ratio = S5P_HDMI_FIN / CEC_DIV_RATIO - 1; + + reg = readl(pmu_regs); + reg = (reg & ~(0x3FF << 16)) | (div_ratio << 16); + writel(reg, pmu_regs); + + div_val = CEC_DIV_RATIO * 0.00005 - 1; + + writeb(0x0, cec_base + S5P_CES_DIVISOR_3); + writeb(0x0, cec_base + S5P_CES_DIVISOR_2); + writeb(0x0, cec_base + S5P_CES_DIVISOR_1); + writeb(div_val, cec_base + S5P_CES_DIVISOR_0); +} + +void s5p_cec_enable_rx(void) +{ + u8 reg; + + reg = readb(cec_base + S5P_CES_RX_CTRL); + reg |= S5P_CES_RX_CTRL_ENABLE; + writeb(reg, cec_base + S5P_CES_RX_CTRL); +} + +void s5p_cec_mask_rx_interrupts(void) +{ + u8 reg; + + reg = readb(cec_base + S5P_CES_IRQ_MASK); + reg |= S5P_CES_IRQ_RX_DONE; + reg |= S5P_CES_IRQ_RX_ERROR; + writeb(reg, cec_base + S5P_CES_IRQ_MASK); +} + +void s5p_cec_unmask_rx_interrupts(void) +{ + u8 reg; + + reg = readb(cec_base + S5P_CES_IRQ_MASK); + reg &= ~S5P_CES_IRQ_RX_DONE; + reg &= ~S5P_CES_IRQ_RX_ERROR; + writeb(reg, cec_base + S5P_CES_IRQ_MASK); +} + +void s5p_cec_mask_tx_interrupts(void) +{ + u8 reg; + reg = readb(cec_base + S5P_CES_IRQ_MASK); + reg |= S5P_CES_IRQ_TX_DONE; + reg |= S5P_CES_IRQ_TX_ERROR; + writeb(reg, cec_base + S5P_CES_IRQ_MASK); + +} + +void s5p_cec_unmask_tx_interrupts(void) +{ + u8 reg; + + reg = readb(cec_base + S5P_CES_IRQ_MASK); + reg &= ~S5P_CES_IRQ_TX_DONE; + reg &= ~S5P_CES_IRQ_TX_ERROR; + writeb(reg, cec_base + S5P_CES_IRQ_MASK); +} + +void s5p_cec_reset(void) +{ + u8 reg; + + writeb(S5P_CES_RX_CTRL_RESET, cec_base + S5P_CES_RX_CTRL); + writeb(S5P_CES_TX_CTRL_RESET, cec_base + S5P_CES_TX_CTRL); + + reg = readb(cec_base + 0xc4); + reg &= ~0x1; + writeb(reg, cec_base + 0xc4); +} + +void s5p_cec_tx_reset(void) +{ + writeb(S5P_CES_TX_CTRL_RESET, cec_base + S5P_CES_TX_CTRL); +} + +void s5p_cec_rx_reset(void) +{ + u8 reg; + + writeb(S5P_CES_RX_CTRL_RESET, cec_base + S5P_CES_RX_CTRL); + + reg = readb(cec_base + 0xc4); + reg &= ~0x1; + writeb(reg, cec_base + 0xc4); +} + +void s5p_cec_threshold(void) +{ + writeb(CEC_FILTER_THRESHOLD, cec_base + S5P_CES_RX_FILTER_TH); + writeb(0, cec_base + S5P_CES_RX_FILTER_CTRL); +} + +void s5p_cec_set_tx_state(enum cec_state state) +{ + atomic_set(&cec_tx_struct.state, state); +} + +void s5p_cec_set_rx_state(enum cec_state state) +{ + atomic_set(&cec_rx_struct.state, state); +} + +void s5p_cec_copy_packet(char *data, size_t count) +{ + int i = 0; + u8 reg; + + while (i < count) { + writeb(data[i], cec_base + (S5P_CES_TX_BUFF0 + (i * 4))); + i++; + } + + writeb(count, cec_base + S5P_CES_TX_BYTES); + s5p_cec_set_tx_state(STATE_TX); + reg = readb(cec_base + S5P_CES_TX_CTRL); + reg |= S5P_CES_TX_CTRL_START; + + if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST) + reg |= S5P_CES_TX_CTRL_BCAST; + else + reg &= ~S5P_CES_TX_CTRL_BCAST; + + reg |= 0x50; + writeb(reg, cec_base + S5P_CES_TX_CTRL); +} + +void s5p_cec_set_addr(u32 addr) +{ + writeb(addr & 0x0F, cec_base + S5P_CES_LOGIC_ADDR); +} + +u32 s5p_cec_get_status(void) +{ + u32 status = 0; + + status = readb(cec_base + S5P_CES_STATUS_0); + status |= readb(cec_base + S5P_CES_STATUS_1) << 8; + status |= readb(cec_base + S5P_CES_STATUS_2) << 16; + status |= readb(cec_base + S5P_CES_STATUS_3) << 24; + + tvout_dbg("status = 0x%x!\n", status); + + return status; +} + +void s5p_clr_pending_tx(void) +{ + writeb(S5P_CES_IRQ_TX_DONE | S5P_CES_IRQ_TX_ERROR, + cec_base + S5P_CES_IRQ_CLEAR); +} + +void s5p_clr_pending_rx(void) +{ + writeb(S5P_CES_IRQ_RX_DONE | S5P_CES_IRQ_RX_ERROR, + cec_base + S5P_CES_IRQ_CLEAR); +} + +void s5p_cec_get_rx_buf(u32 size, u8 *buffer) +{ + u32 i = 0; + + while (i < size) { + buffer[i] = readb(cec_base + S5P_CES_RX_BUFF0 + (i * 4)); + i++; + } +} + +int s5p_cec_mem_probe(struct platform_device *pdev) +{ + struct device_node *hdmiphy_sys; + struct resource *res; + int ret = 0; + + tvout_dbg("%s\n", __func__); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + tvout_dbg( + "failed to get memory region resource for cec\n"); + return -ENXIO; + } + + cec_base = devm_ioremap_resource(&pdev->dev, res); + if (cec_base == NULL) { + tvout_dbg( + "failed to claim register region for hdmicec\n"); + return -ENOENT; + } + + hdmiphy_sys = of_get_child_by_name(pdev->dev.of_node, "hdmiphy-sys"); + if (!hdmiphy_sys) { + tvout_dbg( "No sys-controller interface for hdmiphy\n"); + return -ENODEV; + } + pmu_regs = of_iomap(hdmiphy_sys, 0); + if (pmu_regs == NULL) { + tvout_dbg("Can't get hdmiphy pmu control register\n"); + return -ENOMEM; + } + + return ret; +} diff --git a/drivers/media/platform/s5p-cec/regs-cec.h b/drivers/media/platform/s5p-cec/regs-cec.h new file mode 100644 index 000000000000..1b5ed095f59f --- /dev/null +++ b/drivers/media/platform/s5p-cec/regs-cec.h @@ -0,0 +1,93 @@ +/* linux/arch/arm/mach-exynos/include/mach/regs-cec.h + * + * Copyright (c) 2010 Samsung Electronics + * http://www.samsung.com/ + * + * CEC register header file for Samsung TVOUT driver + * + * 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. + */ + +#ifndef __ARCH_ARM_REGS_CEC_H +#define __ARCH_ARM_REGS_CEC_H + +/* + * Register part + */ +#define S5P_CES_STATUS_0 (0x0000) +#define S5P_CES_STATUS_1 (0x0004) +#define S5P_CES_STATUS_2 (0x0008) +#define S5P_CES_STATUS_3 (0x000C) +#define S5P_CES_IRQ_MASK (0x0010) +#define S5P_CES_IRQ_CLEAR (0x0014) +#define S5P_CES_LOGIC_ADDR (0x0020) +#define S5P_CES_DIVISOR_0 (0x0030) +#define S5P_CES_DIVISOR_1 (0x0034) +#define S5P_CES_DIVISOR_2 (0x0038) +#define S5P_CES_DIVISOR_3 (0x003C) + +#define S5P_CES_TX_CTRL (0x0040) +#define S5P_CES_TX_BYTES (0x0044) +#define S5P_CES_TX_STAT0 (0x0060) +#define S5P_CES_TX_STAT1 (0x0064) +#define S5P_CES_TX_BUFF0 (0x0080) +#define S5P_CES_TX_BUFF1 (0x0084) +#define S5P_CES_TX_BUFF2 (0x0088) +#define S5P_CES_TX_BUFF3 (0x008C) +#define S5P_CES_TX_BUFF4 (0x0090) +#define S5P_CES_TX_BUFF5 (0x0094) +#define S5P_CES_TX_BUFF6 (0x0098) +#define S5P_CES_TX_BUFF7 (0x009C) +#define S5P_CES_TX_BUFF8 (0x00A0) +#define S5P_CES_TX_BUFF9 (0x00A4) +#define S5P_CES_TX_BUFF10 (0x00A8) +#define S5P_CES_TX_BUFF11 (0x00AC) +#define S5P_CES_TX_BUFF12 (0x00B0) +#define S5P_CES_TX_BUFF13 (0x00B4) +#define S5P_CES_TX_BUFF14 (0x00B8) +#define S5P_CES_TX_BUFF15 (0x00BC) + +#define S5P_CES_RX_CTRL (0x00C0) +#define S5P_CES_RX_STAT0 (0x00E0) +#define S5P_CES_RX_STAT1 (0x00E4) +#define S5P_CES_RX_BUFF0 (0x0100) +#define S5P_CES_RX_BUFF1 (0x0104) +#define S5P_CES_RX_BUFF2 (0x0108) +#define S5P_CES_RX_BUFF3 (0x010C) +#define S5P_CES_RX_BUFF4 (0x0110) +#define S5P_CES_RX_BUFF5 (0x0114) +#define S5P_CES_RX_BUFF6 (0x0118) +#define S5P_CES_RX_BUFF7 (0x011C) +#define S5P_CES_RX_BUFF8 (0x0120) +#define S5P_CES_RX_BUFF9 (0x0124) +#define S5P_CES_RX_BUFF10 (0x0128) +#define S5P_CES_RX_BUFF11 (0x012C) +#define S5P_CES_RX_BUFF12 (0x0130) +#define S5P_CES_RX_BUFF13 (0x0134) +#define S5P_CES_RX_BUFF14 (0x0138) +#define S5P_CES_RX_BUFF15 (0x013C) + +#define S5P_CES_RX_FILTER_CTRL (0x0180) +#define S5P_CES_RX_FILTER_TH (0x0184) + +/* + * Bit definition part + */ +#define S5P_CES_IRQ_TX_DONE (1<<0) +#define S5P_CES_IRQ_TX_ERROR (1<<1) +#define S5P_CES_IRQ_RX_DONE (1<<4) +#define S5P_CES_IRQ_RX_ERROR (1<<5) + +#define S5P_CES_TX_CTRL_START (1<<0) +#define S5P_CES_TX_CTRL_BCAST (1<<1) +#define S5P_CES_TX_CTRL_RETRY (0x04<<4) +#define S5P_CES_TX_CTRL_RESET (1<<7) + +#define S5P_CES_RX_CTRL_ENABLE (1<<0) +#define S5P_CES_RX_CTRL_RESET (1<<7) + +#define S5P_CES_LOGIC_ADDR_MASK (0xF) + +#endif /* __ARCH_ARM_REGS_CEC_H */