/* * 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 #include #include #include #include #include #include #include #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 = val; ll->flags = 1; ll->header_count = 14; ll->data_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); } 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; }