|
@@ -81,3 +81,124 @@ static struct pxafb_mode_info am200_fb_mode_6inch = {
|
|
|
.vsync_len = 25,
|
|
|
.upper_margin = 0,
|
|
|
.lower_margin = 2,
|
|
|
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
|
|
|
+};
|
|
|
+
|
|
|
+static struct pxafb_mach_info am200_fb_info = {
|
|
|
+ .modes = &am200_fb_mode_6inch,
|
|
|
+ .num_modes = 1,
|
|
|
+ .lcd_conn = LCD_TYPE_COLOR_TFT | LCD_PCLK_EDGE_FALL |
|
|
|
+ LCD_AC_BIAS_FREQ(24),
|
|
|
+};
|
|
|
+
|
|
|
+/* register offsets for gpio control */
|
|
|
+#define LED_GPIO_PIN 51
|
|
|
+#define STDBY_GPIO_PIN 48
|
|
|
+#define RST_GPIO_PIN 49
|
|
|
+#define RDY_GPIO_PIN 32
|
|
|
+#define ERR_GPIO_PIN 17
|
|
|
+#define PCBPWR_GPIO_PIN 16
|
|
|
+static int gpios[] = { LED_GPIO_PIN , STDBY_GPIO_PIN , RST_GPIO_PIN,
|
|
|
+ RDY_GPIO_PIN, ERR_GPIO_PIN, PCBPWR_GPIO_PIN };
|
|
|
+static char *gpio_names[] = { "LED" , "STDBY" , "RST", "RDY", "ERR", "PCBPWR" };
|
|
|
+
|
|
|
+static int am200_init_gpio_regs(struct metronomefb_par *par)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(gpios); i++) {
|
|
|
+ err = gpio_request(gpios[i], gpio_names[i]);
|
|
|
+ if (err) {
|
|
|
+ dev_err(&am200_device->dev, "failed requesting "
|
|
|
+ "gpio %s, err=%d\n", gpio_names[i], err);
|
|
|
+ goto err_req_gpio;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ gpio_direction_output(LED_GPIO_PIN, 0);
|
|
|
+ gpio_direction_output(STDBY_GPIO_PIN, 0);
|
|
|
+ gpio_direction_output(RST_GPIO_PIN, 0);
|
|
|
+
|
|
|
+ gpio_direction_input(RDY_GPIO_PIN);
|
|
|
+ gpio_direction_input(ERR_GPIO_PIN);
|
|
|
+
|
|
|
+ gpio_direction_output(PCBPWR_GPIO_PIN, 0);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_req_gpio:
|
|
|
+ while (--i >= 0)
|
|
|
+ gpio_free(gpios[i]);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static void am200_cleanup(struct metronomefb_par *par)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ free_irq(PXA_GPIO_TO_IRQ(RDY_GPIO_PIN), par);
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(gpios); i++)
|
|
|
+ gpio_free(gpios[i]);
|
|
|
+}
|
|
|
+
|
|
|
+static int am200_share_video_mem(struct fb_info *info)
|
|
|
+{
|
|
|
+ /* rough check if this is our desired fb and not something else */
|
|
|
+ if ((info->var.xres != am200_fb_info.modes->xres)
|
|
|
+ || (info->var.yres != am200_fb_info.modes->yres))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* we've now been notified that we have our new fb */
|
|
|
+ am200_board.metromem = info->screen_base;
|
|
|
+ am200_board.host_fbinfo = info;
|
|
|
+
|
|
|
+ /* try to refcount host drv since we are the consumer after this */
|
|
|
+ if (!try_module_get(info->fbops->owner))
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int am200_unshare_video_mem(struct fb_info *info)
|
|
|
+{
|
|
|
+ dev_dbg(&am200_device->dev, "ENTER %s\n", __func__);
|
|
|
+
|
|
|
+ if (info != am200_board.host_fbinfo)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ module_put(am200_board.host_fbinfo->fbops->owner);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int am200_fb_notifier_callback(struct notifier_block *self,
|
|
|
+ unsigned long event, void *data)
|
|
|
+{
|
|
|
+ struct fb_event *evdata = data;
|
|
|
+ struct fb_info *info = evdata->info;
|
|
|
+
|
|
|
+ dev_dbg(&am200_device->dev, "ENTER %s\n", __func__);
|
|
|
+
|
|
|
+ if (event == FB_EVENT_FB_REGISTERED)
|
|
|
+ return am200_share_video_mem(info);
|
|
|
+ else if (event == FB_EVENT_FB_UNREGISTERED)
|
|
|
+ return am200_unshare_video_mem(info);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct notifier_block am200_fb_notif = {
|
|
|
+ .notifier_call = am200_fb_notifier_callback,
|
|
|
+};
|
|
|
+
|
|
|
+/* this gets called as part of our init. these steps must be done now so
|
|
|
+ * that we can use pxa_set_fb_info */
|
|
|
+static void __init am200_presetup_fb(void)
|
|
|
+{
|
|
|
+ int fw;
|
|
|
+ int fh;
|
|
|
+ int padding_size;
|
|
|
+ int totalsize;
|
|
|
+
|