mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-11 13:27:06 +09:00
s5p-cec: Add legacy s5p-cec driver
This driver supports Pulse8 libcec library. Change-Id: Ic6189187ec930a59ffac024a5c5436270f7e83a6
This commit is contained in:
committed by
Brian Kim
parent
cbdab82ac8
commit
4feec5ce16
@@ -67,6 +67,13 @@
|
||||
samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
|
||||
samsung,pin-drv = <EXYNOS5420_PIN_DRV_LV1>;
|
||||
};
|
||||
|
||||
hdmi_cec: hdmi_cec {
|
||||
samsung,pins = "gpx3-6";
|
||||
samsung,pin-function = <EXYNOS_PIN_FUNC_3>;
|
||||
samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
|
||||
samsung,pin-drv = <EXYNOS5420_PIN_DRV_LV4>;
|
||||
};
|
||||
};
|
||||
|
||||
&pinctrl_1 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
|
||||
6
drivers/media/platform/s5p-cec/Kconfig
Normal file
6
drivers/media/platform/s5p-cec/Kconfig
Normal file
@@ -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.
|
||||
2
drivers/media/platform/s5p-cec/Makefile
Normal file
2
drivers/media/platform/s5p-cec/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_EXYNOS_HDMI_CEC) += s5p-hdmi_cec.o
|
||||
s5p-hdmi_cec-y += hdmi_cec.o hdmi_cec_ctrl.o
|
||||
86
drivers/media/platform/s5p-cec/cec.h
Normal file
86
drivers/media/platform/s5p-cec/cec.h
Normal file
@@ -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_ */
|
||||
421
drivers/media/platform/s5p-cec/hdmi_cec.c
Normal file
421
drivers/media/platform/s5p-cec/hdmi_cec.c
Normal file
@@ -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 <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#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);
|
||||
247
drivers/media/platform/s5p-cec/hdmi_cec_ctrl.c
Normal file
247
drivers/media/platform/s5p-cec/hdmi_cec_ctrl.c
Normal file
@@ -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 <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
93
drivers/media/platform/s5p-cec/regs-cec.h
Normal file
93
drivers/media/platform/s5p-cec/regs-cec.h
Normal file
@@ -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 */
|
||||
Reference in New Issue
Block a user