📄 sm501.c
字号:
/* * linux/drivers/video/sm501.c * * * Based on pxafb.c Copyright (C) Eric A. Thomas * Based on sa1100fb.c Copyright (C) Eric A. Thomas * Based on acornfb.c Copyright (C) Russell King. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. * * Silicon Motion SM501 Frame Buffer Driver*/#include <common.h>#if defined(CONFIG_VIDEO_SM501)#include <video_fb.h>#include <sm501.h>#include <sm501acc.h>#include <s3c2440.h>#define FBTYPE_8BIT_INDEX 0#define FBTYPE_8BIT_332RGB 5#define FBTYPE_15BIT_555RGB 1#define FBTYPE_16BIT_565RGB 2#define FBTYPE_32BIT_X888RGB 3#define FBTYPE_24BIT_888RGB 4#define VSYNCTIMEOUT 10000//#define SM501_DEBUG int XAACopyROP[16] ={ ROP_0, /* GXclear */ ROP_DSa, /* GXand */ ROP_SDna, /* GXandReverse */ ROP_S, /* GXcopy */ ROP_DSna, /* GXandInverted */ ROP_D, /* GXnoop */ ROP_DSx, /* GXxor */ ROP_DSo, /* GXor */ ROP_DSon, /* GXnor */ ROP_DSxn, /* GXequiv */ ROP_Dn, /* GXinvert*/ ROP_SDno, /* GXorReverse */ ROP_Sn, /* GXcopyInverted */ ROP_DSno, /* GXorInverted */ ROP_DSan, /* GXnand */ ROP_1 /* GXset */};int XAAPatternROP[16]={ ROP_0, ROP_DPa, ROP_PDna, ROP_P, ROP_DPna, ROP_D, ROP_DPx, ROP_DPo, ROP_DPon, ROP_PDxn, ROP_Dn, ROP_PDno, ROP_Pn, ROP_DPno, ROP_DPan, ROP_1};// Format of mode table record.typedef struct _mode_table_t{ // Horizontal timing. int horizontal_total; int horizontal_display_end; int horizontal_sync_start; int horizontal_sync_width; polarity_t horizontal_sync_polarity; // Vertical timing. int vertical_total; int vertical_display_end; int vertical_sync_start; int vertical_sync_height; polarity_t vertical_sync_polarity; // Refresh timing. long pixel_clock; long horizontal_frequency; long vertical_frequency;} mode_table_t, *pmode_table_t;// Clock value structure.typedef struct clock_select_t{ long mclk; int divider; int shift;} clock_select_t, *pclock_select_t;// Registers necessary to set mode.typedef struct _reg_table_t{ unsigned long clock; unsigned long control; unsigned long fb_width; unsigned long horizontal_total; unsigned long horizontal_sync; unsigned long vertical_total; unsigned long vertical_sync; unsigned long width; unsigned long height; display_t display;} reg_table_t, *preg_table_t;// Panel On/Off constants to use with panelPowerSequence.typedef enum _panel_state_t{ PANEL_OFF, PANEL_ON,}panel_state_t;/* Mode table. */mode_table_t mode_table[] ={ //---------------------------------------------------------------------------------------- // H. H. H. H. H. V. V. V. V. V. Pixel H. V. // tot. disp. sync sync sync tot. disp. sync sync sinc clock freq. freq. // end start wdth polarity end start hght polarity //---------------------------------------------------------------------------------------- // 640 x 480 { 800, 640, 656, 96, NEGATIVE, 525, 480, 490, 2, NEGATIVE, 25175000, 31469, 60 }, { 832, 640, 664, 40, NEGATIVE, 520, 480, 489, 3, NEGATIVE, 31500000, 37861, 72 }, { 840, 640, 656, 64, NEGATIVE, 500, 480, 481, 3, NEGATIVE, 31500000, 37500, 75 }, { 832, 640, 696, 56, NEGATIVE, 509, 480, 481, 3, NEGATIVE, 36000000, 43269, 85 }, // 800 x 600 { 1024, 800, 824, 72, POSITIVE, 625, 600, 601, 2, POSITIVE, 36000000, 35156, 56 }, { 1056, 800, 840, 128, POSITIVE, 628, 600, 601, 4, POSITIVE, 40000000, 37879, 60 }, { 1040, 800, 856, 120, POSITIVE, 666, 600, 637, 6, POSITIVE, 50000000, 48077, 72 }, { 1056, 800, 816, 80, POSITIVE, 625, 600, 601, 3, POSITIVE, 49500000, 46875, 75 }, { 1048, 800, 832, 64, POSITIVE, 631, 600, 601, 3, POSITIVE, 56250000, 53674, 85 }, // 1024 x 768 { 1344, 1024, 1048, 136, NEGATIVE, 806, 768, 771, 6, NEGATIVE, 65000000, 48363, 60 }, { 1328, 1024, 1048, 136, NEGATIVE, 806, 768, 771, 6, NEGATIVE, 75000000, 56476, 70 }, { 1312, 1024, 1040, 96, POSITIVE, 800, 768, 769, 3, POSITIVE, 78750000, 60023, 75 }, { 1376, 1024, 1072, 96, POSITIVE, 808, 768, 769, 3, POSITIVE, 94500000, 68677, 85 }, // End of table. { 0, 0, 0, 0, NEGATIVE, 0, 0, 0, 0, NEGATIVE, 0, 0, 0 },/* // 320 x 240 { 351, 320, 335, 8, NEGATIVE, 263, 240, 254, 2, NEGATIVE, 6000000, 16000, 60 }, // 400 x 300 { 528, 400, 420, 64, NEGATIVE, 314, 300, 301, 2, NEGATIVE, 10000000, 18940, 60 }, // 640 x 480 { 800, 640, 656, 96, NEGATIVE, 525, 480, 490, 2, NEGATIVE, 25175000, 31469, 60 }, { 832, 640, 664, 40, NEGATIVE, 520, 480, 489, 3, NEGATIVE, 31500000, 37861, 72 }, { 840, 640, 656, 64, NEGATIVE, 500, 480, 481, 3, NEGATIVE, 31500000, 37500, 75 }, { 832, 640, 696, 56, NEGATIVE, 509, 480, 481, 3, NEGATIVE, 36000000, 43269, 85 }, // 720 x 540 { 900, 720, 740, 96, POSITIVE, 576, 540, 545, 2, POSITIVE, 31100000, 34560, 60 }, // 800 x 480 { 1056, 800, 864, 56, POSITIVE, 525, 480, 490, 2, POSITIVE, 40000000, 31600, 60 }, // 800 x 600 { 1024, 800, 824, 72, POSITIVE, 625, 600, 601, 2, POSITIVE, 36000000, 35156, 56 }, { 1056, 800, 840, 128, POSITIVE, 628, 600, 601, 4, POSITIVE, 40000000, 37879, 60 }, { 1040, 800, 856, 120, POSITIVE, 666, 600, 637, 6, POSITIVE, 50000000, 48077, 72 }, { 1056, 800, 816, 80, POSITIVE, 625, 600, 601, 3, POSITIVE, 49500000, 46875, 75 }, { 1048, 800, 832, 64, POSITIVE, 631, 600, 601, 3, POSITIVE, 56250000, 53674, 85 }, // 1024 x 600 { 1344, 1024, 1048, 136, POSITIVE, 628, 600, 601, 4, POSITIVE, 50600000, 37679, 60 }, // 1024 x 768 { 1344, 1024, 1048, 136, NEGATIVE, 806, 768, 771, 6, NEGATIVE, 65000000, 48363, 60 }, { 1328, 1024, 1048, 136, NEGATIVE, 806, 768, 771, 6, NEGATIVE, 75000000, 56476, 70 }, { 1312, 1024, 1040, 96, POSITIVE, 800, 768, 769, 3, POSITIVE, 78750000, 60023, 75 }, { 1376, 1024, 1072, 96, POSITIVE, 808, 768, 769, 3, POSITIVE, 94500000, 68677, 85 }, // 1280 x 960 { 1688, 1280, 1328, 112, POSITIVE, 1000, 960, 962, 3, POSITIVE, 101000000, 60000, 60 }, // 1280 x 1024 { 1688, 1280, 1328, 112, POSITIVE, 1066, 1024, 1025, 3, POSITIVE, 108000000, 63981, 60 }, // End of table. { 0, 0, 0, 0, NEGATIVE, 0, 0, 0, 0, NEGATIVE, 0, 0, 0 },*/};GraphicDevice sm501;struct SM501STRU sm501info;static char *sm501Reg;static char *sm501Mem;u_long regRead32(u_long nOffset){#ifdef SM501_DEBUG u_long ret = *(volatile unsigned long *)(sm501Reg+nOffset); printf("%s: %#lx --> %#lx\n", __FUNCTION__, sm501Reg+nOffset, ret); return ret;#else return *(volatile unsigned long *)(sm501Reg+nOffset);#endif}static int error;void regWrite32(u_long nOffset, u_long nData){ u_long readback;#ifdef SM501_DEBUG if (nOffset < 0x00080800) printf("%s: %#lx <-- %#lx\n", __FUNCTION__, nOffset, nData); *(volatile unsigned long *)(sm501Reg+nOffset) = nData; readback = *(volatile unsigned long *)(sm501Reg+nOffset); if (readback != nData) { printf("Warning: read value %#lx != %#lx write value, trying again!\n", readback, nData); error = 1; }#else int i=0; for(;;) { *(volatile unsigned long *)(sm501Reg+nOffset) = nData; readback = *(volatile unsigned long *)(sm501Reg+nOffset); if (readback == nData) break; else printf("reg = 0x%lx value = 0x%lx!\n", nOffset, nData); i++; if(i > 2) break; }#endif}void memWrite32(u_long nOffset, u_long nData){#ifdef SM501_DEBUG u_long readback; //printf("%s: %#lx <-- %#lx\n", __FUNCTION__, nOffset, nData); *(volatile unsigned long *)(sm501Mem+nOffset) = nData; readback = *(volatile unsigned long *)(sm501Mem+nOffset); if (readback != nData) printf("Warning: %#lx\n", readback);#else *(volatile unsigned long *)(sm501Mem+nOffset) = nData;//printf("%s: %#lx <-- %#lx\n", __FUNCTION__, nOffset, nData);#endif}// Perform a rounded division.long roundDiv(long num, long denom){ /* n / d + 1 / 2 = (2n + d) / 2d */ return (2 * num + denom) / (2 * denom);}// Finds clock closest to the requested.long findClock(long requested_clock, clock_select_t *clock, display_t display){ long mclk; int divider, shift; long best_diff = 999999999; // Try 288MHz and 336MHz clocks. for (mclk = 288000000; mclk <= 336000000; mclk += 48000000) { // For CRT, try dividers 1 and 3, for panel, try divider 5 as well. for (divider = 1; divider <= (display == PANEL ? 5 : 3); divider += 2) { // Try all 8 shift values. for (shift = 0; shift < 8; shift++) { // Calculate difference with requested clock. long diff = roundDiv(mclk, divider << shift) - requested_clock; if (diff < 0) { diff = -diff; } // If the difference is less than the current, use it. if (diff < best_diff) { // Store best difference. best_diff = diff; // Store clock values. clock->mclk = mclk; clock->divider = divider; clock->shift = shift; } } } } // Return best clock. return clock->mclk / (clock->divider << clock->shift);}// Fill the register structure.void setModeRegisters(reg_table_t *register_table, mode_table_t *mode, display_t display, int bpp){ clock_select_t clock; memset(&clock, 0, sizeof(clock)); // Calculate the clock register values. findClock(mode->pixel_clock * 2, &clock, display); if (display == PANEL) { // Set clock value for panel. register_table->clock = (clock.mclk == 288000000 ? FIELD_SET(0, CURRENT_POWER_CLOCK, P2XCLK_SELECT, 288) : FIELD_SET(0, CURRENT_POWER_CLOCK, P2XCLK_SELECT, 336)) | (clock.divider == 1 ? FIELD_SET(0, CURRENT_POWER_CLOCK, P2XCLK_DIVIDER, 1) : (clock.divider == 3 ? FIELD_SET(0, CURRENT_POWER_CLOCK, P2XCLK_DIVIDER, 3) : FIELD_SET(0, CURRENT_POWER_CLOCK, P2XCLK_DIVIDER, 5))) | FIELD_VALUE(0, CURRENT_POWER_CLOCK, P2XCLK_SHIFT, clock.shift); // Set control register value. register_table->control = (mode->vertical_sync_polarity == POSITIVE ? FIELD_SET(0, PANEL_DISPLAY_CTRL, VSYNC_PHASE, ACTIVE_HIGH) : FIELD_SET(0, PANEL_DISPLAY_CTRL, VSYNC_PHASE, ACTIVE_LOW)) | (mode->horizontal_sync_polarity == POSITIVE ? FIELD_SET(0, PANEL_DISPLAY_CTRL, HSYNC_PHASE, ACTIVE_HIGH) : FIELD_SET(0, PANEL_DISPLAY_CTRL, HSYNC_PHASE, ACTIVE_LOW)) | FIELD_SET(0, PANEL_DISPLAY_CTRL, TIMING, ENABLE) | FIELD_SET(0, PANEL_DISPLAY_CTRL, PLANE, ENABLE) | (bpp == 8 ? FIELD_SET(0, PANEL_DISPLAY_CTRL, FORMAT, 8) : (bpp == 16 ? FIELD_SET(0, PANEL_DISPLAY_CTRL, FORMAT, 16) : FIELD_SET(0, PANEL_DISPLAY_CTRL, FORMAT, 32))); // Set timing registers. register_table->horizontal_total = FIELD_VALUE(0, PANEL_HORIZONTAL_TOTAL, TOTAL, mode->horizontal_total - 1) | FIELD_VALUE(0, PANEL_HORIZONTAL_TOTAL, DISPLAY_END, mode->horizontal_display_end - 1); register_table->horizontal_sync = FIELD_VALUE(0, PANEL_HORIZONTAL_SYNC, WIDTH, mode->horizontal_sync_width) | FIELD_VALUE(0, PANEL_HORIZONTAL_SYNC, START, mode->horizontal_sync_start - 1); register_table->vertical_total = FIELD_VALUE(0, PANEL_VERTICAL_TOTAL, TOTAL, mode->vertical_total - 1) | FIELD_VALUE(0, PANEL_VERTICAL_TOTAL, DISPLAY_END, mode->vertical_display_end - 1); register_table->vertical_sync = FIELD_VALUE(0, PANEL_VERTICAL_SYNC, HEIGHT, mode->vertical_sync_height) | FIELD_VALUE(0, PANEL_VERTICAL_SYNC, START, mode->vertical_sync_start - 1); } else { // Set clock value for CRT. register_table->clock = (clock.mclk == 288000000 ? FIELD_SET(0, CURRENT_POWER_CLOCK, V2XCLK_SELECT, 288) : FIELD_SET(0, CURRENT_POWER_CLOCK, V2XCLK_SELECT, 336)) | (clock.divider == 1 ? FIELD_SET(0, CURRENT_POWER_CLOCK, V2XCLK_DIVIDER, 1) : FIELD_SET(0, CURRENT_POWER_CLOCK, V2XCLK_DIVIDER, 3)) | FIELD_VALUE(0, CURRENT_POWER_CLOCK, V2XCLK_SHIFT, clock.shift); // Set control register value. register_table->control = (mode->vertical_sync_polarity == POSITIVE ? FIELD_SET(0, CRT_DISPLAY_CTRL, VSYNC_PHASE, ACTIVE_HIGH) : FIELD_SET(0, CRT_DISPLAY_CTRL, VSYNC_PHASE, ACTIVE_LOW)) | (mode->horizontal_sync_polarity == POSITIVE ? FIELD_SET(0, CRT_DISPLAY_CTRL, HSYNC_PHASE, ACTIVE_HIGH) : FIELD_SET(0, CRT_DISPLAY_CTRL, HSYNC_PHASE, ACTIVE_LOW)) | FIELD_SET(0, CRT_DISPLAY_CTRL, SELECT, CRT) | FIELD_SET(0, CRT_DISPLAY_CTRL, TIMING, ENABLE) | FIELD_SET(0, CRT_DISPLAY_CTRL, PLANE, ENABLE) | (bpp == 8 ? FIELD_SET(0, CRT_DISPLAY_CTRL, FORMAT, 8) : (bpp == 16 ? FIELD_SET(0, CRT_DISPLAY_CTRL, FORMAT, 16) : FIELD_SET(0, CRT_DISPLAY_CTRL, FORMAT, 32))); // Set timing registers. register_table->horizontal_total = FIELD_VALUE(0, CRT_HORIZONTAL_TOTAL, TOTAL, mode->horizontal_total - 1) | FIELD_VALUE(0, CRT_HORIZONTAL_TOTAL, DISPLAY_END, mode->horizontal_display_end - 1); register_table->horizontal_sync = FIELD_VALUE(0, CRT_HORIZONTAL_SYNC, WIDTH, mode->horizontal_sync_width) | FIELD_VALUE(0, CRT_HORIZONTAL_SYNC, START, mode->horizontal_sync_start - 1); register_table->vertical_total = FIELD_VALUE(0, CRT_VERTICAL_TOTAL, TOTAL, mode->vertical_total - 1) | FIELD_VALUE(0, CRT_VERTICAL_TOTAL, DISPLAY_END, mode->vertical_display_end - 1); register_table->vertical_sync = FIELD_VALUE(0, CRT_VERTICAL_SYNC, HEIGHT, mode->vertical_sync_height) | FIELD_VALUE(0, CRT_VERTICAL_SYNC, START, mode->vertical_sync_start - 1); } // Calculate frame buffer width and height. register_table->fb_width = mode->horizontal_display_end * (bpp / 8); register_table->width = mode->horizontal_display_end; register_table->height = mode->vertical_display_end; // Save display type. register_table->display = display;}// Program new power mode.void setPower(unsigned long nGates, unsigned long Clock){ unsigned long gate_reg, clock_reg; unsigned long control_value;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -