blob: 06c787a2bfbd0fa8fa2daaca2ed041183b21f0cb [file] [log] [blame]
/*
* drivers/amlogic/media/vout/vdac/vdac_dev.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
/* Standard Linux headers */
#include <linux/types.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/clk.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/amlogic/media/vout/vdac_dev.h>
#include <linux/amlogic/iomap.h>
#include <linux/io.h>
#include <linux/mutex.h>
#define AMVDAC_NAME "amvdac"
#define AMVDAC_DRIVER_NAME "amvdac"
#define AMVDAC_MODULE_NAME "amvdac"
#define AMVDAC_DEVICE_NAME "amvdac"
#define AMVDAC_CLASS_NAME "amvdac"
struct amvdac_dev_s {
dev_t devt;
struct cdev cdev;
dev_t devno;
struct device *dev;
struct class *clsp;
};
static struct amvdac_dev_s amvdac_dev;
static struct meson_vdac_data *s_vdac_data;
static struct mutex vdac_mutex;
#define HHI_VDAC_CNTL0 0xbd
#define HHI_VDAC_CNTL1 0xbe
#define HHI_VDAC_CNTL0_G12A 0xbb
#define HHI_VDAC_CNTL1_G12A 0xbc
#define VDAC_MODULE_ATV_DEMOD 0x1
#define VDAC_MODULE_DTV_DEMOD 0x2
#define VDAC_MODULE_TVAFE 0x4
#define VDAC_MODULE_CVBS_OUT 0x8
#define VDAC_MODULE_AUDIO_OUT 0x10
static bool vdac_init_succ_flag;
/* priority for module,
* module index: atv demod:0x01; dtv demod:0x02; tvafe:0x4; cvbsout:0x8
*/
static unsigned int pri_flag;
static unsigned int vdac_cntl0_bit9;
module_param(vdac_cntl0_bit9, uint, 0644);
MODULE_PARM_DESC(vdac_cntl0_bit9, "vdac_cntl0_bit9");
static unsigned int vdac_cntl1_bit3;
module_param(vdac_cntl1_bit3, uint, 0644);
MODULE_PARM_DESC(vdac_cntl1_bit3, "vdac_cntl1_bit3");
static unsigned int vdac_cntl0_bit0;
module_param(vdac_cntl0_bit0, uint, 0644);
MODULE_PARM_DESC(vdac_cntl0_bit0, "vdac_cntl0_bit0");
static unsigned int vdac_cntl0_bit10;
module_param(vdac_cntl0_bit10, uint, 0644);
MODULE_PARM_DESC(vdac_cntl0_bit10, "vdac_cntl0_bit10");
static inline uint32_t vdac_hiu_reg_read(uint32_t reg)
{
return aml_read_hiubus(reg);
}
static inline void vdac_hiu_reg_write(uint32_t reg, uint32_t val)
{
return aml_write_hiubus(reg, val);
}
static inline void vdac_hiu_reg_setb(uint32_t reg,
const uint32_t value,
const uint32_t start,
const uint32_t len)
{
aml_write_hiubus(reg, ((aml_read_hiubus(reg) &
~(((1L << (len)) - 1) << (start))) |
(((value) & ((1L << (len)) - 1)) << (start))));
}
static inline uint32_t vdac_hiu_reg_getb(uint32_t reg,
const uint32_t start,
const uint32_t len)
{
uint32_t val;
val = ((vdac_hiu_reg_read(reg) >> (start)) & ((1L << (len)) - 1));
return val;
}
/* adc/dac ref signal,
* module index: atv demod:0x01; dtv demod:0x02; tvafe:0x4; dac:0x8
*/
void ana_ref_cntl0_bit9(bool on, unsigned int module_sel)
{
bool enable = 0;
/*tl1:bandgap en, bc[7] default:0 opened*/
if (s_vdac_data->cpu_id == VDAC_CPU_TL1)
return;
switch (module_sel & 0x1f) {
case VDAC_MODULE_ATV_DEMOD: /* dtv demod */
if (on)
vdac_cntl0_bit9 |= VDAC_MODULE_ATV_DEMOD;
else
vdac_cntl0_bit9 &= ~VDAC_MODULE_ATV_DEMOD;
break;
case VDAC_MODULE_DTV_DEMOD: /* atv demod */
if (on)
vdac_cntl0_bit9 |= VDAC_MODULE_DTV_DEMOD;
else
vdac_cntl0_bit9 &= ~VDAC_MODULE_DTV_DEMOD;
break;
case VDAC_MODULE_TVAFE: /* cvbs in demod */
if (on)
vdac_cntl0_bit9 |= VDAC_MODULE_TVAFE;
else
vdac_cntl0_bit9 &= ~VDAC_MODULE_TVAFE;
break;
case VDAC_MODULE_CVBS_OUT: /* cvbs in demod */
if (on)
vdac_cntl0_bit9 |= VDAC_MODULE_CVBS_OUT;
else
vdac_cntl0_bit9 &= ~VDAC_MODULE_CVBS_OUT;
break;
case VDAC_MODULE_AUDIO_OUT: /* audio out ctrl*/
if (s_vdac_data->cpu_id == VDAC_CPU_TXL ||
s_vdac_data->cpu_id == VDAC_CPU_TXLX) {
if (on)
vdac_cntl0_bit9 |= VDAC_MODULE_AUDIO_OUT;
else
vdac_cntl0_bit9 &= ~VDAC_MODULE_AUDIO_OUT;
}
break;
default:
pr_err("module_sel: 0x%x wrong module index !! ", module_sel);
break;
}
/* pr_info("\nvdac_cntl0_bit9: 0x%x\n", vdac_cntl0_bit9); */
if ((vdac_cntl0_bit9 & 0x1f) == 0)
enable = 0;
else
enable = 1;
if (s_vdac_data->cpu_id == VDAC_CPU_TXL ||
s_vdac_data->cpu_id == VDAC_CPU_TXLX)
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, enable, 9, 1);
else if (s_vdac_data->cpu_id >= VDAC_CPU_G12AB)
vdac_hiu_reg_setb(HHI_VDAC_CNTL0_G12A, !enable, 9, 1);
else
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, !enable, 9, 1);
}
EXPORT_SYMBOL(ana_ref_cntl0_bit9);
/*avin cvbsout signal,
* module index: atv demod:0x01; dtv demod:0x02; tvafe:0x4; dac:0x8
*/
void vdac_out_cntl0_bit10(bool on, unsigned int module_sel)
{
bool enable = 0;
/*bit10 is for bandgap startup setting in g12a*/
if (s_vdac_data->cpu_id >= VDAC_CPU_G12AB)
return;
switch (module_sel & 0xf) {
case VDAC_MODULE_ATV_DEMOD: /* dtv demod */
if (on)
vdac_cntl0_bit10 |= VDAC_MODULE_ATV_DEMOD;
else
vdac_cntl0_bit10 &= ~VDAC_MODULE_ATV_DEMOD;
break;
case VDAC_MODULE_DTV_DEMOD: /* atv demod */
if (on)
vdac_cntl0_bit10 |= VDAC_MODULE_DTV_DEMOD;
else
vdac_cntl0_bit10 &= ~VDAC_MODULE_DTV_DEMOD;
break;
case VDAC_MODULE_TVAFE: /* av in demod */
if (on)
vdac_cntl0_bit10 |= VDAC_MODULE_TVAFE;
else
vdac_cntl0_bit10 &= ~VDAC_MODULE_TVAFE;
break;
case VDAC_MODULE_CVBS_OUT: /* cvbs out demod */
if (on)
vdac_cntl0_bit10 |= VDAC_MODULE_CVBS_OUT;
else
vdac_cntl0_bit10 &= ~VDAC_MODULE_CVBS_OUT;
break;
default:
pr_err("module_sel: 0x%x wrong module index !! ", module_sel);
break;
}
/* pr_info("\vdac_cntl0_bit0: 0x%x\n", vdac_cntl1_bit3); */
if ((vdac_cntl0_bit10 & 0xf) == 0)
enable = 0;
else
enable = 1;
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, enable, 10, 1);
}
EXPORT_SYMBOL(vdac_out_cntl0_bit10);
/*atv cvbsout signal,
* module index: atv demod:0x01; dtv demod:0x02; tvafe:0x4; dac:0x8
*/
void vdac_out_cntl0_bit0(bool on, unsigned int module_sel)
{
bool enable = 0;
switch (module_sel & 0xf) {
case VDAC_MODULE_ATV_DEMOD: /* dtv demod */
if (on)
vdac_cntl0_bit0 |= VDAC_MODULE_ATV_DEMOD;
else
vdac_cntl0_bit0 &= ~VDAC_MODULE_ATV_DEMOD;
break;
case VDAC_MODULE_DTV_DEMOD: /* atv demod */
if (on)
vdac_cntl0_bit0 |= VDAC_MODULE_DTV_DEMOD;
else
vdac_cntl0_bit0 &= ~VDAC_MODULE_DTV_DEMOD;
break;
case VDAC_MODULE_TVAFE: /* av in demod */
if (on)
vdac_cntl0_bit0 |= VDAC_MODULE_TVAFE;
else
vdac_cntl0_bit0 &= ~VDAC_MODULE_TVAFE;
break;
case VDAC_MODULE_CVBS_OUT: /* cvbs in demod */
if (on)
vdac_cntl0_bit0 |= VDAC_MODULE_CVBS_OUT;
else
vdac_cntl0_bit0 &= ~VDAC_MODULE_CVBS_OUT;
break;
default:
pr_err("module_sel: 0x%x wrong module index !! ", module_sel);
break;
}
/* pr_info("\vdac_cntl0_bit0: 0x%x\n", vdac_cntl1_bit3); */
if ((vdac_cntl0_bit0 & 0xf) == 0)
enable = 0;
else
enable = 1;
if (s_vdac_data->cpu_id >= VDAC_CPU_G12AB)
vdac_hiu_reg_setb(HHI_VDAC_CNTL0_G12A, enable, 0, 1);
else
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, enable, 0, 1);
}
EXPORT_SYMBOL(vdac_out_cntl0_bit0);
/* dac out pwd ctl,
* module index: atv demod:0x01; dtv demod:0x02; tvafe:0x4; dac:0x8
*/
void vdac_out_cntl1_bit3(bool on, unsigned int module_sel)
{
bool enable = 0;
switch (module_sel & 0xf) {
case VDAC_MODULE_ATV_DEMOD: /* atv demod */
if (on)
vdac_cntl1_bit3 |= VDAC_MODULE_ATV_DEMOD;
else
vdac_cntl1_bit3 &= ~VDAC_MODULE_ATV_DEMOD;
break;
case VDAC_MODULE_DTV_DEMOD: /* dtv demod */
if (on)
vdac_cntl1_bit3 |= VDAC_MODULE_DTV_DEMOD;
else
vdac_cntl1_bit3 &= ~VDAC_MODULE_DTV_DEMOD;
break;
case VDAC_MODULE_TVAFE: /* av in demod */
if (on)
vdac_cntl1_bit3 |= VDAC_MODULE_TVAFE;
else
vdac_cntl1_bit3 &= ~VDAC_MODULE_TVAFE;
break;
case VDAC_MODULE_CVBS_OUT: /* cvbs in demod */
if (on)
vdac_cntl1_bit3 |= VDAC_MODULE_CVBS_OUT;
else
vdac_cntl1_bit3 &= ~VDAC_MODULE_CVBS_OUT;
break;
default:
pr_err("module_sel: 0x%x wrong module index !! ", module_sel);
break;
}
/* pr_info("\nvdac_cntl1_bit3: 0x%x\n", vdac_cntl1_bit3); */
if ((vdac_cntl1_bit3 & 0xf) == 0)
enable = 0;
else
enable = 1;
if (s_vdac_data->cpu_id == VDAC_CPU_TXL ||
s_vdac_data->cpu_id == VDAC_CPU_TXLX)
vdac_hiu_reg_setb(HHI_VDAC_CNTL1, enable, 3, 1);
else if (s_vdac_data->cpu_id >= VDAC_CPU_G12AB)
vdac_hiu_reg_setb(HHI_VDAC_CNTL1_G12A, !enable, 3, 1);
else
vdac_hiu_reg_setb(HHI_VDAC_CNTL1, !enable, 3, 1);
}
EXPORT_SYMBOL(vdac_out_cntl1_bit3);
void vdac_set_ctrl0_ctrl1(unsigned int ctrl0, unsigned int ctrl1)
{
if (!s_vdac_data) {
pr_err("\n%s: s_vdac_data NULL\n", __func__);
return;
}
if (s_vdac_data->cpu_id >= VDAC_CPU_G12AB) {
vdac_hiu_reg_write(HHI_VDAC_CNTL0_G12A, ctrl0);
vdac_hiu_reg_write(HHI_VDAC_CNTL1_G12A, ctrl1);
} else {
vdac_hiu_reg_write(HHI_VDAC_CNTL0, ctrl0);
vdac_hiu_reg_write(HHI_VDAC_CNTL1, ctrl1);
}
}
EXPORT_SYMBOL(vdac_set_ctrl0_ctrl1);
/* dac ctl,
* module index: atv demod:0x01; dtv demod:0x02; tvafe:0x4; dac:0x8
*/
void vdac_enable(bool on, unsigned int module_sel)
{
if (!s_vdac_data) {
pr_err("\n%s: s_vdac_data NULL\n", __func__);
return;
}
pr_info("\n%s: on:%d,module_sel:%x\n", __func__, on, module_sel);
mutex_lock(&vdac_mutex);
switch (module_sel) {
case VDAC_MODULE_ATV_DEMOD: /* atv demod */
if ((on && (pri_flag & VDAC_MODULE_ATV_DEMOD))
|| (!on && !(pri_flag & VDAC_MODULE_ATV_DEMOD))) {
pr_info("%s: ATV DEMOD had done!:%d.\n", __func__, on);
break;
}
if (on) {
ana_ref_cntl0_bit9(1, VDAC_MODULE_ATV_DEMOD);
/*after txlx need reset bandgap after bit9 enabled*/
/*bit10 reset bandgap in g12a*/
if (s_vdac_data->cpu_id == VDAC_CPU_TXLX) {
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, 1, 13, 1);
udelay(5);
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, 0, 13, 1);
}
pri_flag &= ~VDAC_MODULE_TVAFE;
pri_flag |= VDAC_MODULE_ATV_DEMOD;
if (pri_flag & VDAC_MODULE_CVBS_OUT)
break;
/*Cdac pwd*/
vdac_out_cntl1_bit3(1, VDAC_MODULE_ATV_DEMOD);
/* enable AFE output buffer */
if (s_vdac_data->cpu_id < VDAC_CPU_G12AB)
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, 0, 10, 1);
vdac_out_cntl0_bit0(1, VDAC_MODULE_ATV_DEMOD);
} else {
ana_ref_cntl0_bit9(0, VDAC_MODULE_ATV_DEMOD);
pri_flag &= ~VDAC_MODULE_ATV_DEMOD;
if (pri_flag & VDAC_MODULE_CVBS_OUT)
break;
vdac_out_cntl0_bit0(0, VDAC_MODULE_ATV_DEMOD);
/* Disable AFE output buffer */
if (s_vdac_data->cpu_id < VDAC_CPU_G12AB)
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, 0, 10, 1);
/* enable dac output */
vdac_out_cntl1_bit3(0, VDAC_MODULE_ATV_DEMOD);
}
break;
case VDAC_MODULE_DTV_DEMOD: /* dtv demod */
if (on) {
if (s_vdac_data->cpu_id == VDAC_CPU_GXLX)
vdac_out_cntl1_bit3(1, VDAC_MODULE_DTV_DEMOD);
ana_ref_cntl0_bit9(1, VDAC_MODULE_DTV_DEMOD);
if (s_vdac_data->cpu_id == VDAC_CPU_TXLX) {
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, 1, 13, 1);
udelay(5);
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, 0, 13, 1);
}
pri_flag |= VDAC_MODULE_DTV_DEMOD;
} else {
ana_ref_cntl0_bit9(0, VDAC_MODULE_DTV_DEMOD);
if (s_vdac_data->cpu_id == VDAC_CPU_GXLX)
vdac_out_cntl1_bit3(0, VDAC_MODULE_DTV_DEMOD);
pri_flag &= ~VDAC_MODULE_DTV_DEMOD;
}
break;
case VDAC_MODULE_TVAFE: /* av in demod */
if (on) {
ana_ref_cntl0_bit9(1, VDAC_MODULE_TVAFE);
/*after txlx need reset bandgap after bit9 enabled*/
if (s_vdac_data->cpu_id == VDAC_CPU_TXLX) {
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, 1, 13, 1);
udelay(5);
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, 0, 13, 1);
}
pri_flag &= ~VDAC_MODULE_ATV_DEMOD;
pri_flag |= VDAC_MODULE_TVAFE;
if (pri_flag & VDAC_MODULE_CVBS_OUT)
break;
vdac_out_cntl1_bit3(0, VDAC_MODULE_TVAFE);
vdac_out_cntl0_bit10(1, VDAC_MODULE_TVAFE);
if (s_vdac_data->cpu_id == VDAC_CPU_TL1) {
/*[6][8]bypass buffer enable*/
vdac_hiu_reg_setb(HHI_VDAC_CNTL1_G12A, 1, 6, 1);
vdac_hiu_reg_setb(HHI_VDAC_CNTL1_G12A, 1, 8, 1);
}
} else {
ana_ref_cntl0_bit9(0, VDAC_MODULE_TVAFE);
if (s_vdac_data->cpu_id == VDAC_CPU_TL1) {
/*[6][8]bypass buffer disable*/
vdac_hiu_reg_setb(HHI_VDAC_CNTL1_G12A, 0, 6, 1);
vdac_hiu_reg_setb(HHI_VDAC_CNTL1_G12A, 0, 8, 1);
}
pri_flag &= ~VDAC_MODULE_TVAFE;
if (pri_flag & VDAC_MODULE_CVBS_OUT)
break;
/* Disable AFE output buffer */
vdac_out_cntl0_bit10(0, VDAC_MODULE_TVAFE);
/* enable dac output */
vdac_out_cntl1_bit3(0, VDAC_MODULE_TVAFE);
}
break;
case VDAC_MODULE_CVBS_OUT: /* cvbs in demod */
if (on) {
vdac_out_cntl1_bit3(1, VDAC_MODULE_CVBS_OUT);
vdac_out_cntl0_bit0(1, VDAC_MODULE_CVBS_OUT);
ana_ref_cntl0_bit9(1, VDAC_MODULE_CVBS_OUT);
if (s_vdac_data->cpu_id == VDAC_CPU_TXLX) {
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, 1, 13, 1);
udelay(5);
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, 0, 13, 1);
}
vdac_out_cntl0_bit10(0, VDAC_MODULE_CVBS_OUT);
pri_flag |= VDAC_MODULE_CVBS_OUT;
} else {
/*vdac_out_cntl0_bit10(0, VDAC_MODULE_CVBS_OUT);*/
ana_ref_cntl0_bit9(0, VDAC_MODULE_CVBS_OUT);
vdac_out_cntl0_bit0(0, VDAC_MODULE_CVBS_OUT);
vdac_out_cntl1_bit3(0, VDAC_MODULE_CVBS_OUT);
pri_flag &= ~VDAC_MODULE_CVBS_OUT;
if (pri_flag & VDAC_MODULE_ATV_DEMOD) {
vdac_out_cntl1_bit3(1, VDAC_MODULE_ATV_DEMOD);
if (s_vdac_data->cpu_id < VDAC_CPU_G12AB)
vdac_hiu_reg_setb(HHI_VDAC_CNTL0,
0, 10, 1);
vdac_out_cntl0_bit0(1, VDAC_MODULE_ATV_DEMOD);
} else if (pri_flag & VDAC_MODULE_TVAFE) {
vdac_out_cntl1_bit3(0, VDAC_MODULE_TVAFE);
vdac_out_cntl0_bit10(1, VDAC_MODULE_TVAFE);
} else if (pri_flag & VDAC_MODULE_DTV_DEMOD) {
if (s_vdac_data->cpu_id == VDAC_CPU_GXLX)
vdac_out_cntl1_bit3(1,
VDAC_MODULE_DTV_DEMOD);
ana_ref_cntl0_bit9(1, VDAC_MODULE_DTV_DEMOD);
}
}
break;
case VDAC_MODULE_AUDIO_OUT: /* audio demod */
/*Bandgap optimization*/
if (s_vdac_data->cpu_id == VDAC_CPU_TXHD ||
s_vdac_data->cpu_id == VDAC_CPU_TXLX)
vdac_hiu_reg_setb(HHI_VDAC_CNTL0, 0xe, 3, 5);
if (s_vdac_data->cpu_id == VDAC_CPU_TXL ||
s_vdac_data->cpu_id == VDAC_CPU_TXLX) {
if (on)
ana_ref_cntl0_bit9(1, VDAC_MODULE_AUDIO_OUT);
else
ana_ref_cntl0_bit9(0, VDAC_MODULE_AUDIO_OUT);
}
break;
default:
pr_err("%s:module_sel: 0x%x wrong module index !! "
, __func__, module_sel);
break;
}
mutex_unlock(&vdac_mutex);
}
EXPORT_SYMBOL(vdac_enable);
int vdac_enable_check_dtv(void)
{
return (pri_flag & VDAC_MODULE_DTV_DEMOD) ? 1:0;
}
static int amvdac_open(struct inode *inode, struct file *file)
{
struct amvdac_dev_s *devp;
/* Get the per-device structure that contains this cdev */
devp = container_of(inode->i_cdev, struct amvdac_dev_s, cdev);
file->private_data = devp;
return 0;
}
static int amvdac_release(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
static const struct file_operations amvdac_fops = {
.owner = THIS_MODULE,
.open = amvdac_open,
.release = amvdac_release,
};
struct meson_vdac_data meson_gxtvbb_vdac_data = {
.cpu_id = VDAC_CPU_GXTVBB,
.name = "meson-gxtvbb-vdac",
};
struct meson_vdac_data meson_gx_l_m_vdac_data = {
.cpu_id = VDAC_CPU_GX_L_M,
.name = "meson-gx_l_m-vdac",
};
struct meson_vdac_data meson_txl_vdac_data = {
.cpu_id = VDAC_CPU_TXL,
.name = "meson-txl-vdac",
};
struct meson_vdac_data meson_txlx_vdac_data = {
.cpu_id = VDAC_CPU_TXLX,
.name = "meson-txlx-vdac",
};
struct meson_vdac_data meson_gxlx_vdac_data = {
.cpu_id = VDAC_CPU_GXLX,
.name = "meson-gxlx-vdac",
};
struct meson_vdac_data meson_txhd_vdac_data = {
.cpu_id = VDAC_CPU_TXHD,
.name = "meson-txhd-vdac",
};
struct meson_vdac_data meson_g12ab_vdac_data = {
.cpu_id = VDAC_CPU_G12AB,
.name = "meson-g12ab-vdac",
};
struct meson_vdac_data meson_tl1_vdac_data = {
.cpu_id = VDAC_CPU_TL1,
.name = "meson-tl1-vdac",
};
struct meson_vdac_data meson_sm1_vdac_data = {
.cpu_id = VDAC_CPU_SM1,
.name = "meson-sm1-vdac",
};
static const struct of_device_id meson_vdac_dt_match[] = {
{
.compatible = "amlogic, vdac-gxtvbb",
.data = &meson_gxtvbb_vdac_data,
}, {
.compatible = "amlogic, vdac-gxl",
.data = &meson_gx_l_m_vdac_data,
}, {
.compatible = "amlogic, vdac-gxm",
.data = &meson_gx_l_m_vdac_data,
}, {
.compatible = "amlogic, vdac-txl",
.data = &meson_txl_vdac_data,
}, {
.compatible = "amlogic, vdac-txlx",
.data = &meson_txlx_vdac_data,
}, {
.compatible = "amlogic, vdac-gxlx",
.data = &meson_gxlx_vdac_data,
}, {
.compatible = "amlogic, vdac-txhd",
.data = &meson_txhd_vdac_data,
}, {
.compatible = "amlogic, vdac-g12a",
.data = &meson_g12ab_vdac_data,
}, {
.compatible = "amlogic, vdac-g12b",
.data = &meson_g12ab_vdac_data,
}, {
.compatible = "amlogic, vdac-tl1",
.data = &meson_tl1_vdac_data,
}, {
.compatible = "amlogic, vdac-sm1",
.data = &meson_sm1_vdac_data,
},
{},
};
static int aml_vdac_probe(struct platform_device *pdev)
{
int ret = 0;
const struct of_device_id *match;
struct amvdac_dev_s *devp = &amvdac_dev;
memset(devp, 0, (sizeof(struct amvdac_dev_s)));
match = of_match_device(meson_vdac_dt_match, &pdev->dev);
if (match == NULL) {
pr_err("%s,no matched table\n", __func__);
return -1;
}
s_vdac_data = (struct meson_vdac_data *)match->data;
pr_info("%s:probe start.cpu_id:%d,name:%s\n", __func__,
s_vdac_data->cpu_id, s_vdac_data->name);
ret = alloc_chrdev_region(&devp->devno, 0, 1, AMVDAC_NAME);
if (ret < 0)
goto fail_alloc_region;
devp->clsp = class_create(THIS_MODULE, AMVDAC_CLASS_NAME);
if (IS_ERR(devp->clsp)) {
ret = PTR_ERR(devp->clsp);
goto fail_create_class;
}
cdev_init(&devp->cdev, &amvdac_fops);
devp->cdev.owner = THIS_MODULE;
ret = cdev_add(&devp->cdev, devp->devno, 1);
if (ret)
goto fail_add_cdev;
devp->dev = device_create(devp->clsp, NULL, devp->devno,
NULL, AMVDAC_NAME);
if (IS_ERR(devp->dev)) {
ret = PTR_ERR(devp->dev);
goto fail_create_device;
}
pr_info("%s: ok\n", __func__);
return ret;
fail_create_device:
pr_err("%s: device create error\n", __func__);
cdev_del(&devp->cdev);
fail_add_cdev:
pr_err("%s: add device error\n", __func__);
fail_create_class:
pr_err("%s: class create error\n", __func__);
class_destroy(devp->clsp);
unregister_chrdev_region(devp->devno, 1);
fail_alloc_region:
pr_err("%s: alloc error\n", __func__);
return ret;
}
static int __exit aml_vdac_remove(struct platform_device *pdev)
{
struct amvdac_dev_s *devp = &amvdac_dev;
device_destroy(devp->clsp, devp->devno);
cdev_del(&devp->cdev);
class_destroy(devp->clsp);
unregister_chrdev_region(devp->devno, 1);
pr_info("%s: amvdac_exit.\n", __func__);
return 0;
}
#ifdef CONFIG_PM
static int amvdac_drv_suspend(struct platform_device *pdev,
pm_message_t state)
{
if (s_vdac_data->cpu_id == VDAC_CPU_TXL ||
s_vdac_data->cpu_id == VDAC_CPU_TXLX)
vdac_hiu_reg_write(HHI_VDAC_CNTL0, 0);
else if (s_vdac_data->cpu_id == VDAC_CPU_TL1)
vdac_hiu_reg_setb(HHI_VDAC_CNTL1_G12A, 1, 7, 1);
pr_info("%s: suspend module\n", __func__);
return 0;
}
static int amvdac_drv_resume(struct platform_device *pdev)
{
/*0xbc[7] for bandgap enable: 0:enable,1:disable*/
if (s_vdac_data->cpu_id == VDAC_CPU_TL1)
vdac_hiu_reg_setb(HHI_VDAC_CNTL1_G12A, 0, 7, 1);
pr_info("%s: resume module\n", __func__);
return 0;
}
#endif
static void amvdac_drv_shutdown(struct platform_device *pdev)
{
unsigned int cntl0, cntl1;
pr_info("%s: shutdown module\n", __func__);
cntl0 = 0x0;
if (s_vdac_data->cpu_id == VDAC_CPU_TXL ||
s_vdac_data->cpu_id == VDAC_CPU_TXLX)
cntl1 = 0x0;
else if (s_vdac_data->cpu_id >= VDAC_CPU_TL1)
cntl1 = 0x80;
else
cntl1 = 0x8;
vdac_set_ctrl0_ctrl1(cntl0, cntl1);
}
static struct platform_driver aml_vdac_driver = {
.driver = {
.name = "aml_vdac",
.owner = THIS_MODULE,
.of_match_table = meson_vdac_dt_match,
},
.probe = aml_vdac_probe,
.remove = __exit_p(aml_vdac_remove),
#ifdef CONFIG_PM
.suspend = amvdac_drv_suspend,
.resume = amvdac_drv_resume,
#endif
.shutdown = amvdac_drv_shutdown,
};
static int __init aml_vdac_init(void)
{
pr_info("%s: module init\n", __func__);
vdac_init_succ_flag = 0;
s_vdac_data = NULL;
mutex_init(&vdac_mutex);
if (platform_driver_register(&aml_vdac_driver)) {
pr_err("%s: failed to register vdac driver module\n", __func__);
return -ENODEV;
}
vdac_init_succ_flag = 1;
return 0;
}
static void __exit aml_vdac_exit(void)
{
mutex_destroy(&vdac_mutex);
pr_info("%s: module exit\n", __func__);
platform_driver_unregister(&aml_vdac_driver);
}
postcore_initcall(aml_vdac_init);
module_exit(aml_vdac_exit);
MODULE_DESCRIPTION("AMLOGIC vdac driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Frank Zhao <frank.zhao@amlogic.com>");