Subject: [PATCH] Enable all formats SDVO-TV support Currently SDVO TV only support NTSC-M format. In this patch we introduce all formats available and create seting-format property at init time. When user dynamically chose preferred format by xrandr command, it will refine all modelines provided by SDVO device based on the chosed format, at last instruct SDVO device to execute. Signed-off-by: Ma Ling --- Due to some dependency, KMS vesrion need more polishing and will send out next week. src/i830_sdvo.c | 215 ++++++++++++++++++++++++++++++++++++++++++++------ src/i830_sdvo_regs.h | 1 + 2 files changed, 190 insertions(+), 26 deletions(-) diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c index acfe599..ceca3b2 100644 --- a/src/i830_sdvo.c +++ b/src/i830_sdvo.c @@ -51,6 +51,31 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. #include "i830_sdvo_regs.h" #include "X11/Xatom.h" +static char *tv_format_names[] = { + "NTSC_M" , "NTSC_J" , "NTSC_443", + "PAL_B" , "PAL_D" , "PAL_G" , + "PAL_H" , "PAL_I" , "PAL_M" , + "PAL_N" , "PAL_NC" , "PAL_60" , + "SECAM_B" , "SECAM_D" , "SECAM_G" , + "SECAM_K" , "SECAM_K1", "SECAM_L" , + "SECAM_60" , + "240m_1080i_59", "240m_1080i_60", + "260m_1080i_59", "260m_1080i_60", + "274m_1080i_50", "274m_1080i_59", + "274m_1080i_60", "274m_1080p_23", + "274m_1080p_24", "274m_1080p_25", + "274m_1080p_29", "274m_1080p_30", + "274m_1080p_50", "274m_1080p_59", + "274m_1080p_60", "295m_1080i_50", + "295m_1080p_50", "296m_720p_59" , + "296m_720p_60" , "296m_720p_50" , + "293m_480p_59" , "170m_480i_59" , + "TURB_576i_50" , "TURB_576p_50" , + "EIA_480i_60" , "EIA_480p_60" , +}; + +#define TV_FORMAT_NUM (sizeof(tv_format_names) / sizeof(*tv_format_names)) + /** SDVO driver private structure. */ struct i830_sdvo_priv { /** SDVO device on SDVO I2C bus. */ @@ -109,6 +134,9 @@ struct i830_sdvo_priv { * convenience. */ struct i830_sdvo_tv_format tv_format; + char *tv_format_name; + char *tv_format_supported[TV_FORMAT_NUM]; + int format_supported_num; /** supported encoding mode, used to determine whether HDMI is supported */ struct i830_sdvo_encode encode; @@ -131,7 +159,10 @@ struct i830_sdvo_priv { /** @} */ }; +#define TV_FORMAT_NAME "TV_FORMAT" static Atom broadcast_atom; +static Atom tv_format_atom; +static Atom tv_format_name_atoms[TV_FORMAT_NUM]; static void i830_sdvo_dump(ScrnInfoPtr pScrn); @@ -980,29 +1011,50 @@ static void i830_sdvo_set_avi_infoframe(xf86OutputPtr output, SDVO_HBUF_TX_VSYNC); } +/** + * This function checks the current TV format. + */ +static void +i830_sdvo_check_tv_format(xf86OutputPtr output, char *tv_format_name, + struct i830_sdvo_sdtv_resolution_request *req) +{ + uint64_t format_map = 0; + uint32_t i, size; + + for (i = 0; i < TV_FORMAT_NUM; i++) + if (strcmp(tv_format_name, tv_format_names[i]) == 0) + break; + + if (sizeof(*req) > sizeof(format_map)) + size = sizeof(format_map); + else + size = sizeof(*req); + + memcpy(&format_map, req, size); + format_map = 1 << i; + memcpy(req, &format_map, size); +} + static void i830_sdvo_set_tv_format(xf86OutputPtr output) { ScrnInfoPtr pScrn = output->scrn; I830OutputPrivatePtr intel_output = output->driver_private; struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; - struct i830_sdvo_tv_format *format, unset; + struct i830_sdvo_tv_format format; uint8_t status; - format = &dev_priv->tv_format; - memset(&unset, 0, sizeof(unset)); - if (memcmp(format, &unset, sizeof(*format))) { - xf86DrvMsg(pScrn->scrnIndex, X_INFO, - "%s: Choosing default TV format of NTSC-M\n", - SDVO_NAME(dev_priv)); - format->ntsc_m = 1; - i830_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, format, - sizeof(*format)); - status = i830_sdvo_read_response(output, NULL, 0); - if (status != SDVO_CMD_STATUS_SUCCESS) - xf86DrvMsg(pScrn->scrnIndex, X_INFO, - "%s: Fail to set TV format\n", SDVO_NAME(dev_priv)); - } + memset(&format, 0, sizeof(struct i830_sdvo_tv_format)); + i830_sdvo_check_tv_format(output, dev_priv->tv_format_name, + (struct i830_sdvo_sdtv_resolution_request *) + &format); + + i830_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, &format, + sizeof(format)); + status = i830_sdvo_read_response(output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "%s: Fail to set TV format\n", SDVO_NAME(dev_priv)); } static Bool @@ -1610,6 +1662,15 @@ i830_sdvo_output_setup (xf86OutputPtr output, uint16_t flag) if (i830_sdvo_check_hdmi_encode (output)) name_prefix = "HDMI"; } + else if (flag & SDVO_OUTPUT_YPRPB0) + { + dev_priv->controlled_output = SDVO_OUTPUT_YPRPB0; + output->subpixel_order = SubPixelHorizontalRGB; /* XXX */ + name_prefix="TV"; + dev_priv->is_tv = TRUE; + intel_output->needs_tv_clock = TRUE; + } + else if (flag & SDVO_OUTPUT_SVID0) { dev_priv->controlled_output = SDVO_OUTPUT_SVID0; @@ -1861,23 +1922,36 @@ i830_sdvo_get_tv_mode(DisplayModePtr *head, int width, int height, } /** - * This function checks the current TV format, and chooses a default if - * it hasn't been set. + * This function get all supported TV formats */ static void -i830_sdvo_check_tv_format(xf86OutputPtr output) +i830_sdvo_get_tv_formats(xf86OutputPtr output) { I830OutputPrivatePtr intel_output = output->driver_private; struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; + uint64_t format_map; struct i830_sdvo_tv_format format; + uint32_t i; uint8_t status; - i830_sdvo_write_cmd(output, SDVO_CMD_GET_TV_FORMAT, NULL, 0); + memset(&dev_priv->tv_format, 0, sizeof(struct i830_sdvo_tv_format)); + + i830_sdvo_write_cmd(output, SDVO_CMD_GET_TV_FORMATS_SUPPORT, NULL, 0); status = i830_sdvo_read_response(output, &format, sizeof(format)); if (status != SDVO_CMD_STATUS_SUCCESS) - return; + return; memcpy(&dev_priv->tv_format, &format, sizeof(format)); + memcpy(&format_map, &format, sizeof(format) > sizeof(format_map) ? + sizeof(format_map) : sizeof(format)); + dev_priv->format_supported_num = 0; + for (i = 0 ; i < TV_FORMAT_NUM; i++) + if (format_map & (1 << i)) { + dev_priv->tv_format_supported + [dev_priv->format_supported_num++] = tv_format_names[i]; + } + + dev_priv->tv_format_name = dev_priv->tv_format_supported[0]; } static DisplayModePtr @@ -1891,14 +1965,14 @@ i830_sdvo_get_tv_modes(xf86OutputPtr output) uint8_t status; float refresh = 60; /* XXX */ - i830_sdvo_check_tv_format(output); + memset(&tv_res, 0, sizeof(struct i830_sdvo_sdtv_resolution_request)); + i830_sdvo_check_tv_format(output, dev_priv->tv_format_name, + &tv_res); /* Read the list of supported input resolutions for the selected TV format. */ - memset(&tv_res, 0, sizeof(tv_res)); - memcpy(&tv_res, &dev_priv->tv_format, sizeof(tv_res)); i830_sdvo_write_cmd(output, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT, - &tv_res, sizeof(tv_res)); + &tv_res, sizeof(tv_res)); status = i830_sdvo_read_response(output, res, sizeof(*res)); if (status != SDVO_CMD_STATUS_SUCCESS) return NULL; @@ -1976,6 +2050,7 @@ i830_sdvo_get_crtc(xf86OutputPtr output) } #endif + static void i830_sdvo_create_resources(xf86OutputPtr output) { @@ -1984,8 +2059,39 @@ i830_sdvo_create_resources(xf86OutputPtr output) I830OutputPrivatePtr intel_output = output->driver_private; struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; INT32 broadcast_range[2]; - int err; + int err, i; + + if (dev_priv->is_tv ) { + /* Set up the tv_format property, which takes effect on mode set + * and accepts strings that match exactly + */ + tv_format_atom = MakeAtom(TV_FORMAT_NAME, + sizeof(TV_FORMAT_NAME) - 1, TRUE); + + for (i = 0; i < dev_priv->format_supported_num; i++) + tv_format_name_atoms[i] = MakeAtom ( + dev_priv->tv_format_supported[i], + strlen ( + dev_priv->tv_format_supported[i]), + TRUE); + err = RRConfigureOutputProperty(output->randr_output, tv_format_atom, + TRUE, FALSE, FALSE, + dev_priv->format_supported_num, + (INT32 *) tv_format_name_atoms); + if (err != 0) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "RRConfigureOutputProperty error, %d\n", err); + + err = RRChangeOutputProperty (output->randr_output, tv_format_atom, + XA_ATOM, 32, PropModeReplace, 1, + &tv_format_name_atoms[0], + FALSE, TRUE); + + if (err != 0) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "RRChangeOutputProperty error, %d\n", err); + } /* only R G B are 8bit color mode */ if (pScrn->depth != 24 || /* only 965G and G4X platform */ @@ -2021,6 +2127,18 @@ i830_sdvo_create_resources(xf86OutputPtr output) } } +static int i830_sdvo_tv_mode_lookup(struct i830_sdvo_priv *dev_priv, char *val) +{ + int i; + for (i = 0; i < dev_priv->format_supported_num; i++) { + if (strcmp(dev_priv->tv_format_supported[i], val) == 0) { + dev_priv->tv_format_name = dev_priv->tv_format_supported[i]; + return 0; + } + } + return -1; +} + static Bool i830_sdvo_set_property(xf86OutputPtr output, Atom property, RRPropertyValuePtr value) @@ -2058,8 +2176,49 @@ i830_sdvo_set_property(xf86OutputPtr output, Atom property, i830_sdvo_write_sdvox(output, temp); dev_priv->broadcast_rgb = val; + + return TRUE; } - return TRUE; + + if (property == tv_format_atom) + { + Atom atom; + const char *name; + DisplayModePtr crtc_mode; + + if (value->type != XA_ATOM || value->format != 32 || value->size != 1) + return FALSE; + + memcpy (&atom, value->data, 4); + name = NameForAtom (atom); + + if (i830_sdvo_tv_mode_lookup(dev_priv, (char *)name)) + return FALSE; + + if (pI830->starting) + return TRUE; + + /* TV format change will generate new modelines, try + to probe them and update outputs. */ + xf86ProbeOutputModes(output->scrn, 0, 0); + /* Mirror output modes to scrn mode list */ + xf86SetScrnInfoModes (output->scrn); + + for (crtc_mode = output->probed_modes; crtc_mode; + crtc_mode = crtc_mode->next) { + + if (output->crtc->mode.HDisplay == crtc_mode->HDisplay && + output->crtc->mode.VDisplay == crtc_mode->VDisplay) + break; + } + if (!crtc_mode) + crtc_mode = output->probed_modes; + + xf86CrtcSetMode(output->crtc, crtc_mode, output->crtc->rotation, + output->crtc->x, output->crtc->y); + return TRUE; + } + return FALSE; } static const xf86OutputFuncsRec i830_sdvo_output_funcs = { @@ -2198,6 +2357,7 @@ void i830_find_sdvo_slave(ScrnInfoPtr pScrn, int output_device, *slave_addr = temp_slave_addr; return ; } + Bool i830_sdvo_init(ScrnInfoPtr pScrn, int output_device) { @@ -2327,6 +2487,9 @@ i830_sdvo_init(ScrnInfoPtr pScrn, int output_device) /* Set the input timing to the screen. Assume always input 0. */ i830_sdvo_set_target_input(output, TRUE, FALSE); + if (dev_priv->is_tv) + i830_sdvo_get_tv_formats(output); + i830_sdvo_get_input_pixel_clock_range(output, &dev_priv->pixel_clock_min, &dev_priv->pixel_clock_max); diff --git a/src/i830_sdvo_regs.h b/src/i830_sdvo_regs.h index ab38355..2d02926 100644 --- a/src/i830_sdvo_regs.h +++ b/src/i830_sdvo_regs.h @@ -368,6 +368,7 @@ struct i830_sdvo_tv_format { unsigned int pad:3; } __attribute__((packed)); +#define SDVO_CMD_GET_TV_FORMATS_SUPPORT 0x27 #define SDVO_CMD_GET_TV_FORMAT 0x28 /** This command should be run before SetOutputTimingsPart[12] */ -- 1.5.4.4