blob: b77e289e7caa8329fa455d0a52bd1a66c537906b [file] [log] [blame]
/*
* drivers/amlogic/media/common/arch/registers/register_ops.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.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/media/utils/log.h>
#include <linux/amlogic/media/registers/register_ops.h>
#include <linux/amlogic/media/registers/register.h>
#include <linux/amlogic/cpu_version.h>
static struct chip_register_ops *amports_ops[BUS_MAX];
/*#define DEBUG_REG_OPS*/
#define DEBUG_PRINT_CNT 10
#ifdef DEBUG_REG_OPS
#define CODEC_OPS_START(bus, reg, c) do {\
if (c < DEBUG_PRINT_CNT)\
pr_err("try %s bus.%d,%x, cnt=%d\n", __func__, bus, reg, c);\
} while (0)
#define CODEC_OPS_ERROR(bus, reg, c) do {\
if (c < DEBUG_PRINT_CNT)\
pr_err("failed on %s bus.%d,%x, cnt=%d\n",\
__func__, bus, reg, c);\
} while (0)
#else
#define CODEC_OPS_START(bus, reg, c)
#define CODEC_OPS_ERROR(bus, reg, c)
#endif
int codec_reg_read(u32 bus_type, unsigned int reg)
{
struct chip_register_ops *ops = amports_ops[bus_type];
if (!ops)
return 0;
ops->r_cnt++;
CODEC_OPS_START(bus_type, reg, ops->r_cnt);
/* the AIU fifo short address has been changed on g12a */
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_G12A)
if (bus_type == IO_AIU_BUS &&
reg >= AIU_AIFIFO_CTRL &&
reg <= AIU_MEM_AIFIFO_MEM_CTL)
reg -= 0x80;
if (ops && ops->read)
return ops->read(ops->ext_offset + reg);
CODEC_OPS_ERROR(bus_type, reg, ops->r_cnt);
return 0;
}
EXPORT_SYMBOL(codec_reg_read);
void codec_reg_write(u32 bus_type, unsigned int reg, unsigned int val)
{
struct chip_register_ops *ops = amports_ops[bus_type];
if (!ops)
return;
ops->w_cnt++;
CODEC_OPS_START(bus_type, reg, ops->w_cnt);
/* the AIU fifo short address has been changed on g12a */
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_G12A)
if (bus_type == IO_AIU_BUS &&
reg >= AIU_AIFIFO_CTRL &&
reg <= AIU_MEM_AIFIFO_MEM_CTL)
reg -= 0x80;
if (ops && ops->write)
return ops->write(ops->ext_offset + reg, val);
CODEC_OPS_ERROR(bus_type, reg, ops->w_cnt);
}
EXPORT_SYMBOL(codec_reg_write);
void codec_reg_write_bits(u32 bus_type, unsigned int reg,
u32 val, int start, int len)
{
/*
*#define WRITE_SEC_REG_BITS(r, val, start, len) \
*WRITE_SEC_REG(r, (READ_SEC_REG(r) & ~(((1L<<(len))-1)<<(start)))|\
*((unsigned)((val)&((1L<<(len))-1)) << (start)))
*/
u32 toval = codec_reg_read(bus_type, reg);
u32 mask = (((1L << (len)) - 1) << (start));
toval &= ~mask;
toval |= (val << start) & mask;
codec_reg_write(bus_type, reg, toval);
}
EXPORT_SYMBOL(codec_reg_write_bits);
static int register_reg_onebus_ops(struct chip_register_ops *ops)
{
if (ops->bus_type >= BUS_MAX)
return -1;
pr_debug("register amports ops for bus[%d]\n", ops->bus_type);
if (amports_ops[ops->bus_type] != NULL)
;
/*
*TODO.
*kfree(amports_ops[ops->bus_type]);
*/
amports_ops[ops->bus_type] = ops;
return 0;
}
int register_reg_ops_per_cpu(int cputype,
struct chip_register_ops *sops, int ops_size)
{
struct chip_register_ops *ops;
int i, size = 0;
if (cputype != get_cpu_type()) {
pr_debug("ignore bus ops for cpu=%d\n", cputype);
return 0; /* ignore don't needed firmare. */
}
/*ops = kmalloc(sizeof(struct chip_register_ops) * ops_size,*/
/*GFP_KERNEL);*/
size = sizeof(struct chip_register_ops);
ops = kmalloc_array(ops_size, size, GFP_KERNEL);
if (!ops)
return -ENOMEM;
memcpy(ops, sops, size * ops_size);
for (i = 0; i < ops_size; i++)
register_reg_onebus_ops(&ops[i]);
return 0;
}
EXPORT_SYMBOL(register_reg_ops_per_cpu);
int register_reg_ops_mgr(int cputype[],
struct chip_register_ops *sops_list, int ops_size)
{
int i = 0;
while (cputype[i] > 0) {
register_reg_ops_per_cpu(cputype[i], sops_list, ops_size);
i++;
}
return 0;
}
EXPORT_SYMBOL(register_reg_ops_mgr);
int register_reg_ex_ops_mgr(int cputype[],
struct chip_register_ops *ex_ops_list, int ops_size)
{
int i = 0;
while (cputype[i] > 0) {
register_reg_ops_per_cpu(cputype[i], ex_ops_list, ops_size);
i++;
}
return 0;
}
EXPORT_SYMBOL(register_reg_ex_ops_mgr);