blob: cfa95270391bbfa84390c02ff22c7aae4cb2f908 [file] [log] [blame]
/*
* Copyright (c) 2008, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <debug.h>
#include <reg.h>
#include <stdlib.h>
#include <string.h>
#include <dev/fbcon.h>
#include <kernel/thread.h>
#include <mddi.h>
#include <target/display.h>
#include <platform/timer.h>
#include "mddi_hw.h"
static mddi_llentry *mlist = NULL;
static mddi_llentry *mlist_remote_write = NULL;
#define MDDI_MAX_REV_PKT_SIZE 0x60
#define MDDI_REV_PKT_BUF_SIZE 256
static void *rev_pkt_buf;
/* functions provided by the target specific panel code */
void panel_init(struct mddi_client_caps *client_caps);
void panel_poweron(void);
void panel_backlight(int on);
/* forward decls */
static void mddi_start_update(void);
static int mddi_update_done(void);
static struct fbcon_config fb_cfg = {
.format = FB_FORMAT_RGB565,
.bpp = 16,
.update_start = mddi_start_update,
.update_done = mddi_update_done,
};
static void printcaps(struct mddi_client_caps *c)
{
if ((c->length != 0x4a) || (c->type != 0x42)) {
dprintf(INFO, "bad caps header\n");
memset(c, 0, sizeof(*c));
return;
}
dprintf(INFO, "mddi: bm: %d,%d win %d,%d rgb %x\n",
c->bitmap_width, c->bitmap_height,
c->display_window_width, c->display_window_height, c->rgb_cap);
dprintf(INFO, "mddi: vend %x prod %x\n",
c->manufacturer_name, c->product_code);
}
/* TODO: add timeout */
static int mddi_wait_status(unsigned statmask)
{
while ((readl(MDDI_STAT) & statmask) == 0) ;
return 0;
}
/* TODO: add timeout */
static int mddi_wait_interrupt(unsigned intmask)
{
while ((readl(MDDI_INT) & intmask) == 0) ;
return 0;
}
void mddi_remote_write(unsigned val, unsigned reg)
{
mddi_llentry *ll;
mddi_register_access *ra;
ll = mlist_remote_write;
ra = &(ll->u.r);
ra->length = 14 + 4;
ra->type = TYPE_REGISTER_ACCESS;
ra->client_id = 0;
ra->rw_info = MDDI_WRITE | 1;
ra->crc = 0;
ra->reg_addr = reg;
ra->reg_data[0] = val;
ll->flags = 1;
ll->header_count = 14;
ll->data_count = 4;
ll->data = &ra->reg_data[0];
ll->next = (void *)0;
ll->reserved = 0;
writel((unsigned)ll, MDDI_PRI_PTR);
mddi_wait_status(MDDI_STAT_PRI_LINK_LIST_DONE);
}
#ifdef MDDI_MULTI_WRITE
void
mddi_remote_multiwrite(unsigned *val_list, unsigned reg, unsigned val_count)
{
mddi_llentry *ll;
mddi_register_access *ra;
ll = mlist_remote_write;
ra = &(ll->u.r);
ra->length = 14 + (val_count * 4);
ra->type = TYPE_REGISTER_ACCESS;
ra->client_id = 0;
ra->rw_info = MDDI_WRITE | val_count;
ra->crc = 0;
ra->reg_addr = reg;
memcpy((void *)&ra->reg_data[0], val_list, val_count);
ll->flags = 1;
ll->header_count = 14;
ll->data_count = val_count * 4;
ll->data = &ra->reg_data;
ll->next = (void *)0;
ll->reserved = 0;
writel((unsigned)ll, MDDI_PRI_PTR);
mddi_wait_status(MDDI_STAT_PRI_LINK_LIST_DONE);
}
#endif
static void mddi_start_update(void)
{
writel((unsigned)mlist, MDDI_PRI_PTR);
}
static int mddi_update_done(void)
{
return !!(readl(MDDI_STAT) & MDDI_STAT_PRI_LINK_LIST_DONE);
}
static void mddi_do_cmd(unsigned cmd)
{
writel(cmd, MDDI_CMD);
mddi_wait_interrupt(MDDI_INT_NO_REQ_PKTS_PENDING);
}
static void mddi_init_rev_encap(void)
{
memset(rev_pkt_buf, 0xee, MDDI_REV_PKT_BUF_SIZE);
writel((unsigned)rev_pkt_buf, MDDI_REV_PTR);
writel((unsigned)rev_pkt_buf, MDDI_REV_PTR);
writel(MDDI_REV_PKT_BUF_SIZE, MDDI_REV_SIZE);
writel(MDDI_REV_PKT_BUF_SIZE, MDDI_REV_ENCAP_SZ);
mddi_do_cmd(CMD_FORCE_NEW_REV_PTR);
}
static void mddi_set_auto_hibernate(unsigned on)
{
writel(CMD_POWER_DOWN, MDDI_CMD);
mddi_wait_interrupt(MDDI_INT_IN_HIBERNATION);
mddi_do_cmd(CMD_HIBERNATE | !!on);
}
void mddi_set_caps(mddi_client_caps * c)
{
/* Hardcoding the capability values */
c->length = 74;
c->type = 66;
c->client_id = 0;
c->protocol_ver = 1;
c->min_protocol_ver = 1;
c->data_rate_cap = 400;
c->interface_type_cap = 0;
c->num_alt_displays = 1;
c->postcal_data_rate = 400;
c->bitmap_width = TARGET_XRES;
c->bitmap_height = TARGET_YRES;
c->display_window_width = TARGET_XRES;
c->display_window_height = TARGET_YRES;
c->cmap_size = 0;
c->cmap_rgb_width = 0;
c->rgb_cap = 34592;
c->mono_cap = 0;
c->reserved1 = 0;
c->ycbcr_cap = 0;
c->bayer_cap = 0;
c->alpha_cursor_planes = 0;
c->client_feature_cap = 4489216;
c->max_video_frame_rate_cap = 60;
c->min_video_frame_rate_cap = 0;
c->min_sub_frame_rate = 0;
c->audio_buf_depth = 0;
c->audio_channel_cap = 0;
c->audio_sampe_rate_rap = 0;
c->audio_sample_res = 0;
c->mic_audio_sample_res = 0;
c->mic_sample_rate_cap = 0;
c->keyboard_data_fmt = 0;
c->pointing_device_data_fmt = 0;
c->content_protection_type = 0;
c->manufacturer_name = 53859;
c->product_code = 34594;
c->reserved3 = 0;
c->serial_no = 1;
c->week_of_manufacture = 0;
c->year_of_manufacture = 0;
c->crc = 53536;
}
static void mddi_get_caps(struct mddi_client_caps *caps)
{
unsigned timeout = 100000;
unsigned n;
writel(0xffffffff, MDDI_INT);
mddi_do_cmd(CMD_LINK_ACTIVE);
/* sometimes this will fail -- do it three times for luck... */
mddi_do_cmd(CMD_RTD_MEASURE);
thread_sleep(1); //mdelay(1);
mddi_do_cmd(CMD_RTD_MEASURE);
thread_sleep(1); //mdelay(1);
mddi_do_cmd(CMD_RTD_MEASURE);
thread_sleep(1); //mdelay(1);
mddi_do_cmd(CMD_GET_CLIENT_CAP);
do {
n = readl(MDDI_INT);
}
while (!(n & MDDI_INT_REV_DATA_AVAIL) && (--timeout));
if (timeout == 0)
dprintf(INFO, "timeout\n");
memcpy(caps, rev_pkt_buf, sizeof(struct mddi_client_caps));
}
static unsigned mddi_init_regs(void)
{
mddi_do_cmd(CMD_RESET);
mddi_do_cmd(CMD_PERIODIC_REV_ENC);
writel(0x0001, MDDI_VERSION);
writel(0x3C00, MDDI_BPS);
writel(0x0003, MDDI_SPM);
writel(0x0005, MDDI_TA1_LEN);
writel(0x001A, MDDI_TA2_LEN);
writel(0x0096, MDDI_DRIVE_HI);
writel(0x0050, MDDI_DRIVE_LO);
writel(0x003C, MDDI_DISP_WAKE);
writel(0x0004, MDDI_REV_RATE_DIV);
/* needs to settle for 5uS */
if (readl(MDDI_PAD_CTL) == 0) {
writel(0x08000, MDDI_PAD_CTL);
udelay(5);
}
writel(0xA850F, MDDI_PAD_CTL);
writel(0x60006, MDDI_DRIVER_START_CNT);
mddi_init_rev_encap();
/* disable hibernate */
mddi_do_cmd(CMD_HIBERNATE | 0);
return readl(MDDI_CORE_VER) & 0xffff;
}
struct fbcon_config *mddi_init(void)
{
unsigned n;
struct mddi_client_caps client_caps;
dprintf(INFO, "mddi_init()\n");
rev_pkt_buf = memalign(32, MDDI_REV_PKT_BUF_SIZE);
mlist_remote_write = memalign(32, sizeof(struct mddi_llentry));
n = mddi_init_regs();
dprintf(INFO, "mddi version: 0x%08x\n", n);
//mddi_get_caps(&client_caps);
//if(!(client_caps.length == 0x4a && client_caps.type == 0x42))
{
mddi_set_caps(&client_caps);
}
fb_cfg.width = client_caps.bitmap_width;
fb_cfg.stride = fb_cfg.width;
fb_cfg.height = client_caps.bitmap_height;
panel_init(&client_caps);
panel_backlight(0);
panel_poweron();
/* v > 8? v > 8 && < 0x19 ? */
writel(2, MDDI_TEST);
dprintf(INFO, "panel is %d x %d\n", fb_cfg.width, fb_cfg.height);
fb_cfg.base =
memalign(4096, fb_cfg.width * fb_cfg.height * (fb_cfg.bpp / 8));
mlist = memalign(32, sizeof(mddi_llentry) * (fb_cfg.height / 8));
dprintf(INFO, "FB @ %p mlist @ %x\n", fb_cfg.base, (unsigned)mlist);
for (n = 0; n < (fb_cfg.height / 8); n++) {
unsigned y = n * 8;
unsigned pixels = fb_cfg.width * 8;
mddi_video_stream *vs = &(mlist[n].u.v);
vs->length = sizeof(mddi_video_stream) - 2 + (pixels * 2);
vs->type = TYPE_VIDEO_STREAM;
vs->client_id = 0;
vs->format = 0x5565; // FORMAT_16BPP;
vs->pixattr = PIXATTR_BOTH_EYES | PIXATTR_TO_ALL;
vs->left = 0;
vs->right = fb_cfg.width - 1;
vs->top = y;
vs->bottom = y + 7;
vs->start_x = 0;
vs->start_y = y;
vs->pixels = pixels;
vs->crc = 0;
vs->reserved = 0;
mlist[n].header_count = sizeof(mddi_video_stream) - 2;
mlist[n].data_count = pixels * 2;
mlist[n].reserved = 0;
mlist[n].data = fb_cfg.base + (y * fb_cfg.width * 2);
mlist[n].next = &mlist[n + 1];
mlist[n].flags = 0;
}
mlist[n - 1].flags = 1;
mlist[n - 1].next = 0;
mddi_set_auto_hibernate(1);
mddi_do_cmd(CMD_LINK_ACTIVE);
panel_backlight(1);
return &fb_cfg;
}