blob: 053a3a8d7cadee99cccd6e027e5448e7d1eb9d5e [file] [log] [blame]
/* Copyright (c) 2010, Code Aurora Forum. 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 version 2 and
* only 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*/
#include <linux/unistd.h>
#include "video_core_type.h"
#include "video_core_720p.h"
#define VIDC_720P_VERSION_STRING "VIDC_V1.0"
unsigned long vid_c_base_addr;
void vidc_720p_set_device_virtual_base(void *virt_addr)
{
vid_c_base_addr = (unsigned long)virt_addr;
}
static inline void vidc_720p_write(unsigned long offset, unsigned long val)
{
pr_debug("REG 0x%08lx: write 0x%08lx\n", offset, val);
mb();
iowrite32(val, vid_c_base_addr + offset);
}
static inline unsigned long vidc_720p_read(unsigned long offset)
{
unsigned long val;
mb();
val = ioread32(vid_c_base_addr + offset);
pr_debug("REG 0x%08lx: read 0x%08lx\n", offset, val);
return val;
}
void vidc_720p_init(char **ppsz_version, size_t sz, phys_addr_t phys_addr,
enum vidc_720p_endian_type dma_endian, u32 interrupt_off,
enum vidc_720p_interrupt_level_selection_type interrupt_sel,
u32 interrupt_mask)
{
if (ppsz_version)
*ppsz_version = VIDC_720P_VERSION_STRING;
if (interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL)
vidc_720p_write(0x0504, 0);
else
vidc_720p_write(0x0504, 1);
if (interrupt_off)
vidc_720p_write(0x0500, 1);
else
vidc_720p_write(0x0500, 0);
vidc_720p_write(0x0508, 1);
vidc_720p_write(0x0518, 0);
vidc_720p_write(0x0518, interrupt_mask);
vidc_720p_write(0x0044, dma_endian);
vidc_720p_write(0x0138, 0);
vidc_720p_write(0x0110, 1);
vidc_720p_write(0x000C, sz / 4); /* word size */
vidc_720p_write(0x0014, phys_addr);
vidc_720p_write(0x0020, 0);
vidc_720p_write(0x0000, 1);
}
u32 vidc_720p_do_sw_reset(void)
{
u32 fw_start;
udelay(5);
vidc_720p_write(0x0108, 0);
udelay(5);
vidc_720p_write(0x0134, 0);
udelay(5);
vidc_720p_write(0x0130, 1);
udelay(15);
vidc_720p_write(0x0130, 0);
udelay(5);
fw_start = vidc_720p_read(0x0134);
if (!fw_start) {
pr_debug("VIDC-SW-RESET-FAILS!\n");
return false;
}
return true;
}
u32 vidc_720p_reset_is_success()
{
u32 stagecounter;
stagecounter = vidc_720p_read(0x0414);
stagecounter &= 0xff;
if (stagecounter != 0xe5) {
pr_debug("VIDC-CPU_RESET-FAILS!\n");
vidc_720p_write(0x0108, 0);
msleep(10);
return false;
}
return true;
}
void vidc_720p_start_cpu(enum vidc_720p_endian_type dma_endian,
phys_addr_t icontext_bufferstart, phys_addr_t debug_core_dump_addr,
size_t debug_buffer_size)
{
u32 dbg_info_input0_reg = 0x1;
vidc_720p_write(0x0110, 0);
vidc_720p_write(0x0230, icontext_bufferstart);
vidc_720p_write(0x0044, dma_endian);
if (debug_buffer_size) {
dbg_info_input0_reg = (debug_buffer_size << 0x10)
| (0x2 << 1) | 0x1;
vidc_720p_write(0x0D10, debug_core_dump_addr);
}
vidc_720p_write(0x0D0C, dbg_info_input0_reg);
vidc_720p_write(0x0108, 1);
}
u32 vidc_720p_cpu_start()
{
u32 fw_status;
fw_status = vidc_720p_read(0x0C14);
if (fw_status != 0x02)
return false;
return true;
}
void vidc_720p_stop_fw(void)
{
vidc_720p_write(0x0134, 0);
vidc_720p_write(0x0108, 0);
}
void vidc_720p_get_interrupt_status(u32 *interrupt_status,
u32 *cmd_err_status, u32 *disp_pic_err_status, u32 *op_failed)
{
u32 err_status;
*interrupt_status = vidc_720p_read(0x0514);
err_status = vidc_720p_read(0x0E9C);
*cmd_err_status = err_status & 0xffff;
*disp_pic_err_status = (err_status & 0xffff0000) >> 16;
*op_failed = (vidc_720p_read(0x0EC0) & 0x2) >> 1;
}
void vidc_720p_interrupt_done_clear(void)
{
vidc_720p_write(0x0508, 1);
vidc_720p_write(0x0104, 4);
}
void vidc_720p_submit_command(u32 ch_id, u32 cmd_id)
{
u32 fw_status;
vidc_720p_write(0x0104, ch_id);
vidc_720p_write(0x0D00, cmd_id);
fw_status = vidc_720p_read(0x0C14);
vidc_720p_write(0x0D1C, fw_status);
}
u32 vidc_720p_engine_reset(u32 ch_id, enum vidc_720p_endian_type dma_endian,
enum vidc_720p_interrupt_level_selection_type interrupt_sel,
u32 interrupt_mask)
{
u32 op_done;
u32 counter = 0;
pr_debug("ENG-RESET!!\n");
/* issue the engine reset command */
vidc_720p_submit_command(ch_id, VIDC_720P_CMD_MFC_ENGINE_RESET);
do {
udelay(20);
op_done = vidc_720p_read(0x050C);
counter++;
} while (!op_done && counter < 10);
if (!op_done)
return false; /* reset fails */
/* write invalid channel id */
vidc_720p_write(0x0104, 4);
/* Set INT_PULSE_SEL */
if (interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL)
vidc_720p_write(0x0504, 0);
else
vidc_720p_write(0x0504, 1);
if (!interrupt_mask) {
/* Disable interrupt */
vidc_720p_write(0x0500, 1);
} else {
/* Enable interrupt */
vidc_720p_write(0x0500, 0);
}
/* Clear any pending interrupt */
vidc_720p_write(0x0508, 1);
/* Set INT_ENABLE_REG */
vidc_720p_write(0x0518, interrupt_mask);
/* Sets the DMA endianness */
vidc_720p_write(0x0044, dma_endian);
/* return engine reset success */
return true ;
}
void vidc_720p_set_channel(u32 ch_id, enum vidc_720p_enc_dec_selection_type
enc_dec_sel, enum vidc_720p_codec_type codec, phys_addr_t pi_fw,
size_t firmware_size)
{
u32 std_sel = codec;
vidc_720p_write(0x012C, 0);
if (enc_dec_sel)
std_sel |= 0x10;
vidc_720p_write(0x0100, std_sel);
switch (codec) {
default:
case VIDC_720P_DIVX:
case VIDC_720P_XVID:
case VIDC_720P_MPEG4:
if (enc_dec_sel == VIDC_720P_ENCODER)
vidc_720p_write(0x0200, pi_fw);
else
vidc_720p_write(0x0204, pi_fw);
break;
case VIDC_720P_H264:
if (enc_dec_sel == VIDC_720P_ENCODER)
vidc_720p_write(0x0208, pi_fw);
else
vidc_720p_write(0x020C, pi_fw);
break;
case VIDC_720P_H263:
if (enc_dec_sel == VIDC_720P_ENCODER)
vidc_720p_write(0x0200, pi_fw);
else
vidc_720p_write(0x0218, pi_fw);
break;
case VIDC_720P_VC1:
vidc_720p_write(0x0210, pi_fw);
break;
case VIDC_720P_MPEG2:
vidc_720p_write(0x40293, pi_fw);
break;
}
vidc_720p_write(0x000C, firmware_size / 4); /* word size */
vidc_720p_submit_command(ch_id, VIDC_720P_CMD_CHSET);
}
void vidc_720p_encode_set_profile(u32 profile, u32 level)
{
u32 profile_level = profile|(level << 0x8);
vidc_720p_write(0x0300, profile_level);
}
void vidc_720p_set_frame_size(u32 size_x, u32 size_y)
{
vidc_720p_write(0x0118, size_x);
vidc_720p_write(0x011C, size_y);
}
void vidc_720p_encode_set_fps(u32 rc_frame_rate)
{
vidc_720p_write(0x0D14, rc_frame_rate);
}
void vidc_720p_encode_set_short_header(u32 short_header)
{
vidc_720p_write(0x0318, short_header);
}
void vidc_720p_encode_set_vop_time(u32 vop_time_resolution,
u32 vop_time_increment)
{
u32 enable_vop, vop_timing_reg;
if (!vop_time_resolution)
vidc_720p_write(0x0E00, 0x0);
else {
enable_vop = 0x1;
vop_timing_reg = (enable_vop << 0x1f) |
(vop_time_resolution << 0x10) | vop_time_increment;
vidc_720p_write(0x0E00, vop_timing_reg);
}
}
void vidc_720p_encode_set_hec_period(u32 hec_period)
{
vidc_720p_write(0x0EB0, hec_period);
}
void vidc_720p_encode_set_qp_params(u32 max_qp, u32 min_qp)
{
u32 qp = min_qp | (max_qp << 0x8);
vidc_720p_write(0x0A0C, qp);
}
void vidc_720p_encode_set_rc_config(u32 enable_frame_level_rc,
u32 enable_mb_level_rc_flag, u32 iframe_qp, u32 pframe_qp)
{
u32 rc_config = iframe_qp;
if (enable_frame_level_rc)
rc_config |= (0x1 << 0x9);
if (enable_mb_level_rc_flag)
rc_config |= (0x1 << 0x8);
vidc_720p_write(0x0A00, rc_config);
vidc_720p_write(0x0A04, pframe_qp);
}
void vidc_720p_encode_set_bit_rate(u32 target_bitrate)
{
vidc_720p_write(0x0A08, target_bitrate);
}
void vidc_720p_encoder_set_param_change(u32 enc_param_change)
{
vidc_720p_write(0x0E08, enc_param_change);
}
void vidc_720p_encode_set_control_param(u32 param_val)
{
vidc_720p_write(0x0EC8, param_val);
}
void vidc_720p_encode_set_frame_level_rc_params(u32 reaction_coeff)
{
vidc_720p_write(0x0A10, reaction_coeff);
}
void vidc_720p_encode_set_mb_level_rc_params(u32 dark_region_as_flag,
u32 smooth_region_as_flag, u32 static_region_as_flag,
u32 activity_region_flag)
{
u32 mb_level_rc = 0x0;
if (activity_region_flag)
mb_level_rc |= 0x1;
if (static_region_as_flag)
mb_level_rc |= (0x1 << 0x1);
if (smooth_region_as_flag)
mb_level_rc |= (0x1 << 0x2);
if (dark_region_as_flag)
mb_level_rc |= (0x1 << 0x3);
/* Write MB level rate control */
vidc_720p_write(0x0A14, mb_level_rc);
}
void vidc_720p_encode_set_entropy_control(enum vidc_720p_entropy_sel_type
entropy_sel, enum vidc_720p_cabac_model_type cabac_model_number)
{
u32 num;
u32 entropy_params = entropy_sel;
/* Set Model Number */
if (entropy_sel == VIDC_720P_ENTROPY_SEL_CABAC) {
num = (u32)cabac_model_number;
entropy_params |= (num << 0x2);
}
/* Set Entropy parameters */
vidc_720p_write(0x0310, entropy_params);
}
void vidc_720p_encode_set_db_filter_control(enum vidc_720p_DBConfig_type
db_config, u32 slice_alpha_offset, u32 slice_beta_offset)
{
u32 deblock_params;
deblock_params = db_config;
deblock_params |=
(slice_beta_offset << 0x2) | (slice_alpha_offset << 0x7);
/* Write deblocking control settings */
vidc_720p_write(0x0314, deblock_params);
}
void vidc_720p_encode_set_intra_refresh_mb_number(u32 cir_mb_number)
{
vidc_720p_write(0x0810, cir_mb_number);
}
void vidc_720p_encode_set_multi_slice_info(enum vidc_720p_MSlice_selection_type
m_slice_sel, u32 multi_slice_size)
{
switch (m_slice_sel) {
case VIDC_720P_MSLICE_BY_MB_COUNT:
vidc_720p_write(0x0EA8, 0x1);
vidc_720p_write(0x1517, m_slice_sel);
vidc_720p_write(0x0324, multi_slice_size);
break;
case VIDC_720P_MSLICE_BY_BYTE_COUNT:
vidc_720p_write(0x0EA8, 0x1);
vidc_720p_write(0x1517, m_slice_sel);
vidc_720p_write(0x0328, multi_slice_size);
break;
case VIDC_720P_MSLICE_BY_GOB:
vidc_720p_write(0x0EA8, 0x1);
break;
default:
case VIDC_720P_MSLICE_OFF:
vidc_720p_write(0x0EA8, 0x0);
break;
}
}
void vidc_720p_encode_set_dpb_buffer(dma_addr_t pi_enc_dpb_addr,
size_t alloc_len)
{
vidc_720p_write(0x080C, pi_enc_dpb_addr);
vidc_720p_write(0x0ED4, alloc_len);
}
void vidc_720p_encode_set_i_period(u32 period)
{
vidc_720p_write(0x0308, period);
}
void vidc_720p_encode_init_codec(u32 ch_id,
enum vidc_720p_memory_access_method_type memory_access_model)
{
vidc_720p_write(0x0600, memory_access_model);
vidc_720p_submit_command(ch_id, VIDC_720P_CMD_INITCODEC);
}
void vidc_720p_encode_unalign_bitstream(u32 upper_unalign_word,
u32 lower_unalign_word)
{
vidc_720p_write(0x0EA0, upper_unalign_word);
vidc_720p_write(0x0EA4, lower_unalign_word);
}
void vidc_720p_encode_set_seq_header_buffer(phys_addr_t ext_buffer_start,
phys_addr_t ext_buffer_end, u32 start_byte_num)
{
vidc_720p_write(0x0018, ext_buffer_start);
vidc_720p_write(0x0024, ext_buffer_start);
vidc_720p_write(0x001C, ext_buffer_end);
vidc_720p_write(0x005C, start_byte_num);
}
void vidc_720p_encode_frame(u32 ch_id, phys_addr_t ext_buffer_start,
phys_addr_t ext_buffer_end, u32 start_byte_number, phys_addr_t y_addr,
phys_addr_t c_addr)
{
vidc_720p_write(0x0018, ext_buffer_start);
vidc_720p_write(0x001C, ext_buffer_end);
vidc_720p_write(0x0024, ext_buffer_start);
vidc_720p_write(0x005C, start_byte_number);
vidc_720p_write(0x99105, y_addr);
vidc_720p_write(0x0804, c_addr);
vidc_720p_submit_command(ch_id, VIDC_720P_CMD_FRAMERUN);
}
void vidc_720p_encode_get_header(u32 *pi_enc_header_size)
{
*pi_enc_header_size = vidc_720p_read(0x0060);
}
void vidc_720p_enc_frame_info(struct vidc_720p_enc_frame_info *enc_frame_info)
{
enc_frame_info->enc_size = vidc_720p_read(0x0058);
enc_frame_info->frame_type = vidc_720p_read(0x0EBC);
enc_frame_info->frame_type &= 0x03;
enc_frame_info->metadata_exists = vidc_720p_read(0x0EB8);
}
void vidc_720p_decode_bitstream_header(u32 ch_id, u32 dec_unit_size,
u32 start_byte_num, u32 ext_buffer_start, u32 ext_buffer_end,
enum vidc_720p_memory_access_method_type memory_access_model)
{
vidc_720p_write(0x0E04, 0x0);
vidc_720p_write(0x0018, ext_buffer_start);
vidc_720p_write(0x001C, ext_buffer_end);
vidc_720p_write(0x0024, ext_buffer_end);
vidc_720p_write(0x0054, dec_unit_size);
vidc_720p_write(0x005C, start_byte_num);
vidc_720p_write(0x0600, memory_access_model);
vidc_720p_submit_command(ch_id, VIDC_720P_CMD_INITCODEC);
}
void vidc_720p_decode_get_seq_hdr_info(struct vidc_720p_seq_hdr_info_type
*seq_hdr_info)
{
unsigned long tmp;
seq_hdr_info->img_size_x = vidc_720p_read(0x0118);
seq_hdr_info->img_size_y = vidc_720p_read(0x011C);
seq_hdr_info->min_num_dpb = vidc_720p_read(0x0E10);
seq_hdr_info->min_dpb_size = vidc_720p_read(0x0C10);
seq_hdr_info->dec_frm_size = vidc_720p_read(0x0C08);
tmp = vidc_720p_read(0x0C0C);
seq_hdr_info->profile = tmp & 0x1f;
seq_hdr_info->level = (tmp & 0xff00) >> 8;
tmp = vidc_720p_read(0x0408);
seq_hdr_info->progressive = (tmp & 0x4) >> 2;
/* bit 3 is for crop existence */
seq_hdr_info->crop_exists = (tmp & 0x8) >> 3;
if (seq_hdr_info->crop_exists) {
/* read the cropping information */
tmp = vidc_720p_read(0x0C00);
seq_hdr_info->crop_right_offset = (tmp & 0xffff0000) >> 0x10;
seq_hdr_info->crop_left_offset = tmp & 0xffff;
tmp = vidc_720p_read(0x0C04);
seq_hdr_info->crop_bottom_offset = (tmp & 0xffff0000) >> 0x10;
seq_hdr_info->crop_top_offset = tmp & 0xffff;
}
/* Read the MPEG4 data partitioning indication */
seq_hdr_info->data_partitioned = (vidc_720p_read(0x0EBC) & 0x8) >> 3;
}
void vidc_720p_decode_set_dpb_release_buffer_mask(u32 dpb_release_buffer_mask)
{
vidc_720p_write(0x0E98, dpb_release_buffer_mask);
}
void vidc_720p_decode_set_dpb_buffers(u32 i, phys_addr_t dpb_buffer)
{
vidc_720p_write(0x0E18 + sizeof(i) * i, dpb_buffer);
}
void vidc_720p_decode_set_comv_buffer(phys_addr_t pi_dpb_comv_buffer,
size_t alloc_len)
{
vidc_720p_write(0x0904, pi_dpb_comv_buffer);
vidc_720p_write(0x0D08, alloc_len);
}
void vidc_720p_decode_set_dpb_details(u32 num_dpb, size_t alloc_len,
phys_addr_t ref_buffer)
{
vidc_720p_write(0x0900, ref_buffer);
vidc_720p_write(0x0908, 0);
vidc_720p_write(0x0E14, num_dpb);
vidc_720p_write(0x0ED4, alloc_len);
}
void vidc_720p_decode_set_mpeg4Post_filter(u32 enable_post_filter)
{
if (enable_post_filter)
vidc_720p_write(0x0124, 0x1);
else
vidc_720p_write(0x0124, 0x0);
}
void vidc_720p_decode_set_error_control(u32 enable_error_control)
{
if (enable_error_control)
vidc_720p_write(0x013C, 0);
else
vidc_720p_write(0x013C, 1);
}
void vidc_720p_set_deblock_line_buffer(dma_addr_t pi_deblock_line_buffer_start,
size_t alloc_len)
{
vidc_720p_write(0x0234, pi_deblock_line_buffer_start);
vidc_720p_write(0x0D04, alloc_len);
}
void vidc_720p_decode_set_mpeg4_data_partitionbuffer(dma_addr_t vsp_buf_start)
{
vidc_720p_write(0x0230, vsp_buf_start);
}
void vidc_720p_decode_setH264VSPBuffer(dma_addr_t pi_vsp_temp_buffer_start)
{
vidc_720p_write(0x0230, pi_vsp_temp_buffer_start);
}
void vidc_720p_decode_frame(u32 ch_id, phys_addr_t ext_buffer_start,
phys_addr_t ext_buffer_end, size_t dec_unit_size, u32 start_byte_num,
u32 input_frame_tag)
{
vidc_720p_write(0x0018, ext_buffer_start);
vidc_720p_write(0x001C, ext_buffer_end);
vidc_720p_write(0x0024, ext_buffer_end);
vidc_720p_write(0x005C, start_byte_num);
vidc_720p_write(0x0EE0, input_frame_tag);
vidc_720p_write(0x0054, dec_unit_size);
vidc_720p_submit_command(ch_id, VIDC_720P_CMD_FRAMERUN);
}
void vidc_720p_issue_eos(u32 ch_id)
{
vidc_720p_write(0x0028, 0x1);
vidc_720p_write(0x0054, 0);
vidc_720p_submit_command(ch_id, VIDC_720P_CMD_FRAMERUN);
}
void vidc_720p_eos_info(u32 *disp_status)
{
*disp_status = vidc_720p_read(0x0408) & 0x3;
}
void vidc_720p_decode_display_info(struct vidc_720p_dec_disp_info *disp_info)
{
unsigned long tmp;
tmp = vidc_720p_read(0x0408);
disp_info->disp_status = (enum vidc_720p_display_status_type)
(tmp & 0x3);
disp_info->disp_is_interlace = (tmp & 0x4) >> 2;
disp_info->crop_exists = (tmp & 0x8) >> 3;
disp_info->resl_change = (tmp & 0x30) >> 4;
disp_info->reconfig_flush_done = vidc_720p_read(0x0EC0) & 0x1;
disp_info->img_size_x = vidc_720p_read(0x0118);
disp_info->img_size_y = vidc_720p_read(0x011C);
disp_info->y_addr = vidc_720p_read(0x0400);
disp_info->c_addr = vidc_720p_read(0x0404);
disp_info->tag_top = vidc_720p_read(0x0EA8);
disp_info->tag_bottom = vidc_720p_read(0x0EE4);
disp_info->pic_time_top = vidc_720p_read(0x0ED8);
disp_info->pic_time_bottom = vidc_720p_read(0x0EDC);
if (disp_info->crop_exists) {
tmp = vidc_720p_read(0x0C00);
disp_info->crop_right_offset = (tmp & 0xffff0000) >> 0x10;
disp_info->crop_left_offset = tmp & 0xffff;
tmp = vidc_720p_read(0x0C04);
disp_info->crop_bottom_offset = (tmp & 0xffff0000) >> 0x10;
disp_info->crop_top_offset = tmp & 0xffff;
}
disp_info->metadata_exists = vidc_720p_read(0x0EB8);
disp_info->input_bytes_consumed = vidc_720p_read(0x0C08);
disp_info->input_frame_num = vidc_720p_read(0x0410);
disp_info->input_frame_type = vidc_720p_read(0x0EBC) & 0x7;
disp_info->input_is_interlace = (disp_info->input_frame_type & 0x4) >>
2;
disp_info->input_frame_type &= 0x3;
}
void vidc_720p_decode_skip_frm_details(phys_addr_t *free_luma_dpb)
{
u32 disp_frm_type;
disp_frm_type = vidc_720p_read(0x0EB4);
if (disp_frm_type == VIDC_720P_NOTCODED)
*free_luma_dpb = vidc_720p_read(0x0C18);
}
void vidc_720p_metadata_enable(u32 flag, phys_addr_t input_buffer)
{
vidc_720p_write(0x0EC4, flag);
vidc_720p_write(0x0ED0, input_buffer);
}
void vidc_720p_decode_dynamic_req_reset(void)
{
vidc_720p_write(0x0EE8, 0x0);
vidc_720p_write(0x0EAC, 0x0);
vidc_720p_write(0x0028, 0x0);
}
void vidc_720p_decode_dynamic_req_set(u32 property)
{
if (property == VIDC_720P_FLUSH_REQ)
vidc_720p_write(0x0EE8, 0x1);
else if (property == VIDC_720P_EXTRADATA)
vidc_720p_write(0x0EAC, 0x1);
}
void vidc_720p_decode_setpassthrough_start(phys_addr_t pass_startaddr)
{
vidc_720p_write(0x0D18, pass_startaddr);
}