blob: 12baca770f9209ccd394e04f2e9f61f6cf2f8dbe [file] [log] [blame]
/*
* linux/drivers/char/ti81xx_hdmi/ti81xx_hdmi.c
*
* Copyright (C) 2009 Texas Instruments
*
*
* 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.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/cdev.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/ti81xxhdmi.h>
#include "ti81xx_hdmi_cfg.h"
#define TI81XX_HDMI_DRIVER_NAME "TI81XX_HDMI"
struct ti81xx_hdmi_params
{
/* Handle to library */
void* hdmi_lib_handle;
/* Other parameters */
u32 wp_v_addr;
u32 core_v_addr;
u32 phy_v_addr;
u32 prcm_v_addr;
u32 venc_v_addr;
#ifndef CONFIG_ARCH_TI816X
u32 hdmi_pll_v_addr;
#endif
int i;
};
/* Global var */
struct ti81xx_hdmi_params hdmi_obj;
static struct ti81xx_hdmi_init_params initParams;
/* Module param */
static int hdmi_mode = -1;
static int ti81xx_hdmi_major;
static struct cdev ti81xx_hdmi_cdev;
static dev_t ti81xx_hdmi_dev_id;
static struct device *ti81xx_hdmi_device;
static struct class *ti81xx_hdmi_class = NULL;
static void ti81xx_hdmi_platform_release(struct device *device);
static int ti81xx_hdmi_probe(struct device *device);
static int ti81xx_hdmi_remove(struct device *device);
static int ti81xx_hdmi_open(struct inode *inode, struct file *filp);
static int ti81xx_hdmi_release(struct inode *inode, struct file *filp);
static long ti81xx_hdmi_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);
static struct file_operations ti81xx_hdmi_fops = {
.owner = THIS_MODULE,
.open = ti81xx_hdmi_open,
.release = ti81xx_hdmi_release,
.unlocked_ioctl = ti81xx_hdmi_ioctl,
};
static struct device_driver ti81xx_hdmi_driver = {
.name = TI81XX_HDMI_DRIVER_NAME,
.bus = &platform_bus_type,
.probe = ti81xx_hdmi_probe,
.remove = ti81xx_hdmi_remove,
};
static struct platform_device ti81xx_hdmi_plat_device = {
.name = TI81XX_HDMI_DRIVER_NAME,
.id = 2,
.dev = {
.release = ti81xx_hdmi_platform_release,
}
};
/*
* ti81xx_hdmi_open: This function opens hdmi driver.
*/
static int ti81xx_hdmi_open(struct inode *inode, struct file *filp)
{
int ret = 0;
/* Call library to open HDMI */
hdmi_obj.hdmi_lib_handle = ti81xx_hdmi_lib_open(0, &ret, 0x0);
if ((ret == 0x0) && (hdmi_obj.hdmi_lib_handle != NULL)) {
THDBG("TI81xx_hdmi: Opend\n");
filp->private_data = &hdmi_obj;
}
else{
printk("TI81xx_hdmi: Could not open %d %p\n",
ret, hdmi_obj.hdmi_lib_handle);
}
return ret;
}
/*
* ti81xx_hdmi_release: This function releases hdmi driver.
*/
static int ti81xx_hdmi_release(struct inode *inode, struct file *filp)
{
int ret = 0;
struct ti81xx_hdmi_params *params =
(struct ti81xx_hdmi_params *)filp->private_data;
THDBG("TI81xx_hdmi: Release\n");
/* Call close of the library */
if (!ret) {
if (params) {
/* kfree(params);*/
filp->private_data = NULL;
}
}
return ret;
}
/*
* ti81xx_hdmi_ioctl: This function will process IOCTL commands sent by
* the application.
*/
static long ti81xx_hdmi_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct ti81xx_hdmi_params *params =
(struct ti81xx_hdmi_params *)file->private_data;
void * handle = params->hdmi_lib_handle;
THDBG("TI81xx_hdmi: Ioctl\n");
return (long)(ti81xx_hdmi_lib_control(handle, cmd, (void *)arg, NULL));
}
static void ti81xx_hdmi_platform_release(struct device *device)
{
/* this is called when the reference count goes to zero */
}
static int ti81xx_hdmi_probe(struct device *device)
{
THDBG("TI81xx_hdmi: probe\n");
return 0;
}
static int ti81xx_hdmi_remove(struct device *device)
{
THDBG("TI81xx_hdmi: remove\n");
return 0;
}
/**
* ti81xx_hdmi_init() - Initialize TI81XX HDMI Driver
*/
int __init ti81xx_hdmi_init(void)
{
int result;
/* Get the major number for this module */
result = alloc_chrdev_region(&ti81xx_hdmi_dev_id, 0, 1, TI81XX_HDMI_DRIVER_NAME);
if (result) {
printk("TI81xx_HDMI: Cound not register region\n");
goto err_exit;
}
ti81xx_hdmi_major = MAJOR(ti81xx_hdmi_dev_id);
/* printk("Major Number %d MinorNumber %d\n", MAJOR(ti81xx_hdmi_dev_id), MINOR(ti81xx_hdmi_dev_id));*/
/* initialize character device */
cdev_init(&ti81xx_hdmi_cdev, &ti81xx_hdmi_fops);
ti81xx_hdmi_cdev.owner = THIS_MODULE;
ti81xx_hdmi_cdev.ops = &ti81xx_hdmi_fops;
/* add char driver */
result = cdev_add(&ti81xx_hdmi_cdev, ti81xx_hdmi_dev_id, 1);
if (result) {
printk("TI81xx_hdmi: Could not add hdmi char driver\n");
goto err_remove_region;
}
/* register driver as a platform driver */
result = driver_register(&ti81xx_hdmi_driver);
if (result) {
printk("TI81xx_hdmi: Cound register driver\n");
goto err_remove_cdev;
}
/* register the drive as a platform device */
result = platform_device_register(&ti81xx_hdmi_plat_device);
if (result) {
printk("TI81xx_hdmi: Cound register as platform device\n");
goto err_driver_unregister;
}
ti81xx_hdmi_class = class_create(THIS_MODULE, TI81XX_HDMI_DRIVER_NAME);
if (IS_ERR(ti81xx_hdmi_class)) {
result = -EIO;
printk("TI81xx_hdmi: Could not create class\n");
goto err_remove_platform_device;
}
ti81xx_hdmi_device = device_create(ti81xx_hdmi_class, NULL,
ti81xx_hdmi_dev_id, NULL,
TI81XX_HDMI_DRIVER_NAME);
if(IS_ERR(ti81xx_hdmi_device)) {
result = -EIO;
printk("TI81xx_hdmi: Cound not create device file\n");
goto err_remove_class;
}
hdmi_obj.prcm_v_addr = (int) ioremap(PRCM_0_REGS, 0x500);
if (hdmi_obj.prcm_v_addr == 0x0){
printk("TI81xx_hdmi: Could not ioremap for PRCM\n");
goto err_remove_class;
} else {
THDBG("PRCM at address %x\n", hdmi_obj.prcm_v_addr);
}
/* Initialize the global strucutres... */
hdmi_obj.hdmi_lib_handle = NULL;
hdmi_obj.wp_v_addr = (int) ioremap(HDMI_WP_0_REGS, 512);
if (hdmi_obj.wp_v_addr == 0x0){
printk("TI81xx_hdmi: Could not ioremap for WP\n");
goto err_remove_class;
} else {
THDBG("Wrapper at address %x\n", hdmi_obj.wp_v_addr);
}
hdmi_obj.core_v_addr = (int) ioremap(HDMI_CORE_0_REGS, 2560);
if (hdmi_obj.core_v_addr == 0x0){
printk("TI81xx_hdmi: Could not ioremap for Core\n");
goto err_remove_class;
} else {
THDBG("Core at address %x\n", hdmi_obj.core_v_addr);
}
#if 0
hdmi_obj.phy_v_addr = kmalloc(512, GFP_KERNEL);
if (!hdmi_obj.phy_v_addr){
printk("TI81xx_hdmi: Could not ioremap for PHY\n");
goto err_remove_class;
} else {
THDBG("PHY at address %x\n", hdmi_obj.phy_v_addr);
}
#else
hdmi_obj.phy_v_addr = (int) ioremap(HDMI_PHY_0_REGS, 64);
if (hdmi_obj.phy_v_addr == 0x0){
printk("TI81xx_hdmi: Could not ioremap for PHY\n");
goto err_remove_class;
} else {
THDBG("PHY at address %x\n", hdmi_obj.phy_v_addr);
}
#endif
hdmi_obj.venc_v_addr = (volatile u32) ioremap(0x48106000, 0x80);
if (hdmi_obj.venc_v_addr == 0x0){
printk("TI81xx_hdmi: Could not ioremap for Venc\n");
goto err_remove_class;
} else {
THDBG("PHY at address %x\n", hdmi_obj.venc_v_addr);
}
#ifndef CONFIG_ARCH_TI816X
hdmi_obj.hdmi_pll_v_addr = (volatile u32) ioremap(0x481c5200, 0x80);
if (hdmi_obj.hdmi_pll_v_addr == 0x0){
printk("TI81xx_hdmi: Could not ioremap for HDMI PLL\n");
goto err_remove_class;
} else {
THDBG("HDMI PLL at address %x\n", hdmi_obj.hdmi_pll_v_addr);
}
#endif
/* Initialize the HDMI library */
initParams.wp_base_addr = (u32) hdmi_obj.wp_v_addr;
initParams.core_base_addr = (u32) hdmi_obj.core_v_addr;
initParams.phy_base_addr = (u32) hdmi_obj.phy_v_addr;
initParams.prcm_base_addr = (u32) hdmi_obj.prcm_v_addr;
initParams.venc_base_addr = (u32) hdmi_obj.venc_v_addr;
#ifndef CONFIG_ARCH_TI816X
initParams.hdmi_pll_base_addr = (u32) hdmi_obj.hdmi_pll_v_addr;
#endif
/* Set the HDMI user to proper value if not set correctly */
if (hdmi_mode != -1 && (hdmi_mode < 0 || hdmi_mode >= hdmi_max_mode))
{
hdmi_mode = hdmi_1080P_60_mode;
}
if (ti81xx_hdmi_lib_init(&initParams, hdmi_mode) != 0x0){
printk("TI81xx_hdmi: Init failed\n");
goto err_remove_class;
}
THDBG("TI81xx_hdmi: Initialized \n");
return 0;
err_remove_class:
class_destroy(ti81xx_hdmi_class);
err_remove_platform_device:
platform_device_unregister(&ti81xx_hdmi_plat_device);
err_driver_unregister:
driver_unregister(&ti81xx_hdmi_driver);
err_remove_cdev:
cdev_del(&ti81xx_hdmi_cdev);
err_remove_region:
unregister_chrdev_region(ti81xx_hdmi_dev_id, 1);
err_exit:
return result;
}
/**
* ti81xx_hdmi_exit() - Perform clean before unload
*/
void __exit ti81xx_hdmi_exit(void)
{
ti81xx_hdmi_lib_deinit(NULL);
device_destroy(ti81xx_hdmi_class, ti81xx_hdmi_dev_id);
class_destroy(ti81xx_hdmi_class);
platform_device_unregister(&ti81xx_hdmi_plat_device);
driver_unregister(&ti81xx_hdmi_driver);
cdev_del(&ti81xx_hdmi_cdev);
unregister_chrdev_region(ti81xx_hdmi_dev_id, 1);
iounmap((int *)hdmi_obj.wp_v_addr);
iounmap((int *)hdmi_obj.core_v_addr);
iounmap((int *)hdmi_obj.phy_v_addr);
iounmap((int *)hdmi_obj.prcm_v_addr);
iounmap((int *)hdmi_obj.venc_v_addr);
#ifndef CONFIG_ARCH_TI816X
iounmap((int *)hdmi_obj.hdmi_pll_v_addr);
#endif
}
module_param_named(hdmi_mode, hdmi_mode, int, 0664);
module_init(ti81xx_hdmi_init);
module_exit(ti81xx_hdmi_exit);
MODULE_LICENSE("GPL");