diff options
Diffstat (limited to 'drivers/gpu')
99 files changed, 3982 insertions, 1099 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index e19520c4b4b6..d9c88d13f8db 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -1106,6 +1106,10 @@ static void amdgpu_uvd_idle_work_handler(struct work_struct *work) if (fences == 0 && handles == 0) { if (adev->pm.dpm_enabled) { amdgpu_dpm_enable_uvd(adev, false); + /* just work around for uvd clock remain high even + * when uvd dpm disabled on Polaris10 */ + if (adev->asic_type == CHIP_POLARIS10) + amdgpu_asic_set_uvd_clocks(adev, 0, 0); } else { amdgpu_asic_set_uvd_clocks(adev, 0, 0); } diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_i2c.c b/drivers/gpu/drm/amd/amdgpu/atombios_i2c.c index 13cdb01e9b45..bc56c8a181e6 100644 --- a/drivers/gpu/drm/amd/amdgpu/atombios_i2c.c +++ b/drivers/gpu/drm/amd/amdgpu/atombios_i2c.c @@ -156,3 +156,18 @@ u32 amdgpu_atombios_i2c_func(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } +void amdgpu_atombios_i2c_channel_trans(struct amdgpu_device* adev, u8 slave_addr, u8 line_number, u8 offset, u8 data) +{ + PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction); + + args.ucRegIndex = offset; + args.lpI2CDataOut = data; + args.ucFlag = 1; + args.ucI2CSpeed = TARGET_HW_I2C_CLOCK; + args.ucTransBytes = 1; + args.ucSlaveAddr = slave_addr; + args.ucLineNumber = line_number; + + amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args); +} diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_i2c.h b/drivers/gpu/drm/amd/amdgpu/atombios_i2c.h index d6128d9de56e..251aaf41f65d 100644 --- a/drivers/gpu/drm/amd/amdgpu/atombios_i2c.h +++ b/drivers/gpu/drm/amd/amdgpu/atombios_i2c.h @@ -27,5 +27,7 @@ int amdgpu_atombios_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num); u32 amdgpu_atombios_i2c_func(struct i2c_adapter *adap); +void amdgpu_atombios_i2c_channel_trans(struct amdgpu_device* adev, + u8 slave_addr, u8 line_number, u8 offset, u8 data); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index c30b6ac25d89..aabaf2f92575 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -28,6 +28,7 @@ #include "vid.h" #include "amdgpu_ucode.h" #include "amdgpu_atombios.h" +#include "atombios_i2c.h" #include "clearstate_vi.h" #include "gmc/gmc_8_2_d.h" @@ -47,6 +48,8 @@ #include "dce/dce_10_0_d.h" #include "dce/dce_10_0_sh_mask.h" +#include "smu/smu_7_1_3_d.h" + #define GFX8_NUM_GFX_RINGS 1 #define GFX8_NUM_COMPUTE_RINGS 8 @@ -282,6 +285,7 @@ static const u32 golden_settings_polaris11_a11[] = mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000f3, mmTCP_CHAN_STEER_HI, 0xffffffff, 0x00000000, mmTCP_CHAN_STEER_LO, 0xffffffff, 0x00003210, + mmVGT_RESET_DEBUG, 0x00000004, 0x00000004, }; static const u32 polaris11_golden_common_all[] = @@ -312,6 +316,7 @@ static const u32 golden_settings_polaris10_a11[] = mmTCC_CTRL, 0x00100000, 0xf31fff7f, mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000f7, mmTCP_CHAN_STEER_HI, 0xffffffff, 0x00000000, + mmVGT_RESET_DEBUG, 0x00000004, 0x00000004, }; static const u32 polaris10_golden_common_all[] = @@ -693,6 +698,11 @@ static void gfx_v8_0_init_golden_registers(struct amdgpu_device *adev) amdgpu_program_register_sequence(adev, polaris10_golden_common_all, (const u32)ARRAY_SIZE(polaris10_golden_common_all)); + WREG32_SMC(ixCG_ACLK_CNTL, 0x0000001C); + if (adev->pdev->revision == 0xc7) { + amdgpu_atombios_i2c_channel_trans(adev, 0x10, 0x96, 0x1E, 0xDD); + amdgpu_atombios_i2c_channel_trans(adev, 0x10, 0x96, 0x1F, 0xD0); + } break; case CHIP_CARRIZO: amdgpu_program_register_sequence(adev, diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.c index 9d764c4d253e..a6c9b4201e25 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.c @@ -780,7 +780,7 @@ static int polaris10_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr, table->Smio[level] |= data->mvdd_voltage_table.entries[level].smio_low; } - table->SmioMask2 = data->vddci_voltage_table.mask_low; + table->SmioMask2 = data->mvdd_voltage_table.mask_low; table->MvddLevelCount = (uint32_t) PP_HOST_TO_SMC_UL(count); } @@ -1470,22 +1470,19 @@ static int polaris10_populate_smc_acpi_level(struct pp_hwmgr *hwmgr, table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC; - if (!data->sclk_dpm_key_disabled) { - /* Get MinVoltage and Frequency from DPM0, - * already converted to SMC_UL */ - sclk_frequency = data->dpm_table.sclk_table.dpm_levels[0].value; - result = polaris10_get_dependency_volt_by_clk(hwmgr, - table_info->vdd_dep_on_sclk, - table->ACPILevel.SclkFrequency, - &table->ACPILevel.MinVoltage, &mvdd); - PP_ASSERT_WITH_CODE((0 == result), - "Cannot find ACPI VDDC voltage value " - "in Clock Dependency Table", ); - } else { - sclk_frequency = data->vbios_boot_state.sclk_bootup_value; - table->ACPILevel.MinVoltage = - data->vbios_boot_state.vddc_bootup_value * VOLTAGE_SCALE; - } + + /* Get MinVoltage and Frequency from DPM0, + * already converted to SMC_UL */ + sclk_frequency = data->dpm_table.sclk_table.dpm_levels[0].value; + result = polaris10_get_dependency_volt_by_clk(hwmgr, + table_info->vdd_dep_on_sclk, + sclk_frequency, + &table->ACPILevel.MinVoltage, &mvdd); + PP_ASSERT_WITH_CODE((0 == result), + "Cannot find ACPI VDDC voltage value " + "in Clock Dependency Table", + ); + result = polaris10_calculate_sclk_params(hwmgr, sclk_frequency, &(table->ACPILevel.SclkSetting)); PP_ASSERT_WITH_CODE(result == 0, "Error retrieving Engine Clock dividers from VBIOS.", return result); @@ -1510,24 +1507,18 @@ static int polaris10_populate_smc_acpi_level(struct pp_hwmgr *hwmgr, CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw1_frac); CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Sclk_ss_slew_rate); - if (!data->mclk_dpm_key_disabled) { - /* Get MinVoltage and Frequency from DPM0, already converted to SMC_UL */ - table->MemoryACPILevel.MclkFrequency = - data->dpm_table.mclk_table.dpm_levels[0].value; - result = polaris10_get_dependency_volt_by_clk(hwmgr, - table_info->vdd_dep_on_mclk, - table->MemoryACPILevel.MclkFrequency, - &table->MemoryACPILevel.MinVoltage, &mvdd); - PP_ASSERT_WITH_CODE((0 == result), - "Cannot find ACPI VDDCI voltage value " - "in Clock Dependency Table", - ); - } else { - table->MemoryACPILevel.MclkFrequency = - data->vbios_boot_state.mclk_bootup_value; - table->MemoryACPILevel.MinVoltage = - data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE; - } + + /* Get MinVoltage and Frequency from DPM0, already converted to SMC_UL */ + table->MemoryACPILevel.MclkFrequency = + data->dpm_table.mclk_table.dpm_levels[0].value; + result = polaris10_get_dependency_volt_by_clk(hwmgr, + table_info->vdd_dep_on_mclk, + table->MemoryACPILevel.MclkFrequency, + &table->MemoryACPILevel.MinVoltage, &mvdd); + PP_ASSERT_WITH_CODE((0 == result), + "Cannot find ACPI VDDCI voltage value " + "in Clock Dependency Table", + ); us_mvdd = 0; if ((POLARIS10_VOLTAGE_CONTROL_NONE == data->mvdd_control) || @@ -1572,6 +1563,7 @@ static int polaris10_populate_smc_vce_level(struct pp_hwmgr *hwmgr, struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table = table_info->mm_dep_table; struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend); + uint32_t vddci; table->VceLevelCount = (uint8_t)(mm_table->count); table->VceBootLevel = 0; @@ -1581,9 +1573,18 @@ static int polaris10_populate_smc_vce_level(struct pp_hwmgr *hwmgr, table->VceLevel[count].MinVoltage = 0; table->VceLevel[count].MinVoltage |= (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT; + + if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) + vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table), + mm_table->entries[count].vddc - VDDC_VDDCI_DELTA); + else if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) + vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA; + else + vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT; + + table->VceLevel[count].MinVoltage |= - ((mm_table->entries[count].vddc - data->vddc_vddci_delta) * - VOLTAGE_SCALE) << VDDCI_SHIFT; + (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT; table->VceLevel[count].MinVoltage |= 1 << PHASES_SHIFT; /*retrieve divider value for VBIOS */ @@ -1612,6 +1613,7 @@ static int polaris10_populate_smc_samu_level(struct pp_hwmgr *hwmgr, struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table = table_info->mm_dep_table; struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend); + uint32_t vddci; table->SamuBootLevel = 0; table->SamuLevelCount = (uint8_t)(mm_table->count); @@ -1622,8 +1624,16 @@ static int polaris10_populate_smc_samu_level(struct pp_hwmgr *hwmgr, table->SamuLevel[count].Frequency = mm_table->entries[count].samclock; table->SamuLevel[count].MinVoltage |= (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT; - table->SamuLevel[count].MinVoltage |= ((mm_table->entries[count].vddc - - data->vddc_vddci_delta) * VOLTAGE_SCALE) << VDDCI_SHIFT; + + if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) + vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table), + mm_table->entries[count].vddc - VDDC_VDDCI_DELTA); + else if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) + vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA; + else + vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT; + + table->SamuLevel[count].MinVoltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT; table->SamuLevel[count].MinVoltage |= 1 << PHASES_SHIFT; /* retrieve divider value for VBIOS */ @@ -1706,6 +1716,7 @@ static int polaris10_populate_smc_uvd_level(struct pp_hwmgr *hwmgr, struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table = table_info->mm_dep_table; struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend); + uint32_t vddci; table->UvdLevelCount = (uint8_t)(mm_table->count); table->UvdBootLevel = 0; @@ -1716,8 +1727,16 @@ static int polaris10_populate_smc_uvd_level(struct pp_hwmgr *hwmgr, table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk; table->UvdLevel[count].MinVoltage |= (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT; - table->UvdLevel[count].MinVoltage |= ((mm_table->entries[count].vddc - - data->vddc_vddci_delta) * VOLTAGE_SCALE) << VDDCI_SHIFT; + + if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) + vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table), + mm_table->entries[count].vddc - VDDC_VDDCI_DELTA); + else if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) + vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA; + else + vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT; + + table->UvdLevel[count].MinVoltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT; table->UvdLevel[count].MinVoltage |= 1 << PHASES_SHIFT; /* retrieve divider value for VBIOS */ @@ -1738,8 +1757,8 @@ static int polaris10_populate_smc_uvd_level(struct pp_hwmgr *hwmgr, CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency); CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency); CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].MinVoltage); - } + return result; } @@ -1839,20 +1858,26 @@ static int polaris10_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr) for (i = 0; i < sclk_table->count; i++) { data->smc_state_table.Sclk_CKS_masterEn0_7 |= sclk_table->entries[i].cks_enable << i; - - volt_without_cks = (uint32_t)(((ro - 40) * 1000 - 2753594 - sclk_table->entries[i].clk/100 * 136418 /1000) / \ - (sclk_table->entries[i].clk/100 * 1132925 /10000 - 242418)/100); - - volt_with_cks = (uint32_t)((ro * 1000 -2396351 - sclk_table->entries[i].clk/100 * 329021/1000) / \ - (sclk_table->entries[i].clk/10000 * 649434 /1000 - 18005)/10); + if (hwmgr->chip_id == CHIP_POLARIS10) { + volt_without_cks = (uint32_t)((2753594000U + (sclk_table->entries[i].clk/100) * 136418 -(ro - 70) * 1000000) / \ + (2424180 - (sclk_table->entries[i].clk/100) * 1132925/1000)); + volt_with_cks = (uint32_t)((2797202000U + sclk_table->entries[i].clk/100 * 3232 - (ro - 65) * 1000000) / \ + (2522480 - sclk_table->entries[i].clk/100 * 115764/100)); + } else { + volt_without_cks = (uint32_t)((2416794800U + (sclk_table->entries[i].clk/100) * 1476925/10 -(ro - 50) * 1000000) / \ + (2625416 - (sclk_table->entries[i].clk/100) * (12586807/10000))); + volt_with_cks = (uint32_t)((2999656000U - sclk_table->entries[i].clk/100 * 392803 - (ro - 44) * 1000000) / \ + (3422454 - sclk_table->entries[i].clk/100 * (18886376/10000))); + } if (volt_without_cks >= volt_with_cks) volt_offset = (uint8_t)(((volt_without_cks - volt_with_cks + - sclk_table->entries[i].cks_voffset) * 100 / 625) + 1); + sclk_table->entries[i].cks_voffset) * 100 + 624) / 625); data->smc_state_table.Sclk_voltageOffset[i] = volt_offset; } + data->smc_state_table.LdoRefSel = (table_info->cac_dtp_table->ucCKS_LDO_REFSEL != 0) ? table_info->cac_dtp_table->ucCKS_LDO_REFSEL : 6; /* Populate CKS Lookup Table */ if (stretch_amount == 1 || stretch_amount == 2 || stretch_amount == 5) stretch_amount2 = 0; @@ -2629,6 +2654,8 @@ int polaris10_enable_dpm_tasks(struct pp_hwmgr *hwmgr) PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to enable VR hot GPIO interrupt!", result = tmp_result); + smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)PPSMC_HasDisplay); + tmp_result = polaris10_enable_sclk_control(hwmgr); PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to enable SCLK control!", result = tmp_result); @@ -2847,7 +2874,7 @@ static int polaris10_get_evv_voltages(struct pp_hwmgr *hwmgr) { struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend); uint16_t vv_id; - uint16_t vddc = 0; + uint32_t vddc = 0; uint16_t i, j; uint32_t sclk = 0; struct phm_ppt_v1_information *table_info = @@ -2878,8 +2905,9 @@ static int polaris10_get_evv_voltages(struct pp_hwmgr *hwmgr) continue; } - /* need to make sure vddc is less than 2v or else, it could burn the ASIC. */ - PP_ASSERT_WITH_CODE((vddc < 2000 && vddc != 0), + /* need to make sure vddc is less than 2v or else, it could burn the ASIC. + * real voltage level in unit of 0.01mv */ + PP_ASSERT_WITH_CODE((vddc < 200000 && vddc != 0), "Invalid VDDC value", result = -EINVAL;); /* the voltage should not be zero nor equal to leakage ID */ @@ -3105,6 +3133,31 @@ static int polaris10_set_private_data_based_on_pptable(struct pp_hwmgr *hwmgr) return 0; } +int polaris10_patch_voltage_workaround(struct pp_hwmgr *hwmgr) +{ + struct phm_ppt_v1_information *table_info = + (struct phm_ppt_v1_information *)(hwmgr->pptable); + struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table = + table_info->vdd_dep_on_mclk; + struct phm_ppt_v1_voltage_lookup_table *lookup_table = + table_info->vddc_lookup_table; + uint32_t i; + + if (hwmgr->chip_id == CHIP_POLARIS10 && hwmgr->hw_revision == 0xC7) { + if (lookup_table->entries[dep_mclk_table->entries[dep_mclk_table->count-1].vddInd].us_vdd >= 1000) + return 0; + + for (i = 0; i < lookup_table->count; i++) { + if (lookup_table->entries[i].us_vdd < 0xff01 && lookup_table->entries[i].us_vdd >= 1000) { + dep_mclk_table->entries[dep_mclk_table->count-1].vddInd = (uint8_t) i; + return 0; + } + } + } + return 0; +} + + int polaris10_hwmgr_backend_init(struct pp_hwmgr *hwmgr) { struct polaris10_hwmgr *data; @@ -3188,6 +3241,7 @@ int polaris10_hwmgr_backend_init(struct pp_hwmgr *hwmgr) polaris10_set_features_platform_caps(hwmgr); + polaris10_patch_voltage_workaround(hwmgr); polaris10_init_dpm_defaults(hwmgr); /* Get leakage voltage based on leakage ID. */ @@ -4557,6 +4611,15 @@ static int polaris10_notify_link_speed_change_after_state_change( return 0; } +static int polaris10_notify_smc_display(struct pp_hwmgr *hwmgr) +{ + struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend); + + smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, + (PPSMC_Msg)PPSMC_MSG_SetVBITimeout, data->frame_time_x2); + return (smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)PPSMC_HasDisplay) == 0) ? 0 : -EINVAL; +} + static int polaris10_set_power_state_tasks(struct pp_hwmgr *hwmgr, const void *input) { int tmp_result, result = 0; @@ -4605,6 +4668,11 @@ static int polaris10_set_power_state_tasks(struct pp_hwmgr *hwmgr, const void *i "Failed to program memory timing parameters!", result = tmp_result); + tmp_result = polaris10_notify_smc_display(hwmgr); + PP_ASSERT_WITH_CODE((0 == tmp_result), + "Failed to notify smc display settings!", + result = tmp_result); + tmp_result = polaris10_unfreeze_sclk_mclk_dpm(hwmgr); PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to unfreeze SCLK MCLK DPM!", @@ -4639,6 +4707,7 @@ static int polaris10_set_max_fan_pwm_output(struct pp_hwmgr *hwmgr, uint16_t us_ PPSMC_MSG_SetFanPwmMax, us_max_fan_pwm); } + int polaris10_notify_smc_display_change(struct pp_hwmgr *hwmgr, bool has_display) { PPSMC_Msg msg = has_display ? (PPSMC_Msg)PPSMC_HasDisplay : (PPSMC_Msg)PPSMC_NoDisplay; @@ -4658,8 +4727,6 @@ int polaris10_notify_smc_display_config_after_ps_adjustment(struct pp_hwmgr *hwm if (num_active_displays > 1) /* to do && (pHwMgr->pPECI->displayConfiguration.bMultiMonitorInSync != TRUE)) */ polaris10_notify_smc_display_change(hwmgr, false); - else - polaris10_notify_smc_display_change(hwmgr, true); return 0; } @@ -4700,6 +4767,8 @@ int polaris10_program_display_gap(struct pp_hwmgr *hwmgr) frame_time_in_us = 1000000 / refresh_rate; pre_vbi_time_in_us = frame_time_in_us - 200 - mode_info.vblank_time_us; + data->frame_time_x2 = frame_time_in_us * 2 / 100; + display_gap2 = pre_vbi_time_in_us * (ref_clock / 100); cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL2, display_gap2); @@ -4708,8 +4777,6 @@ int polaris10_program_display_gap(struct pp_hwmgr *hwmgr) cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, data->soft_regs_start + offsetof(SMU74_SoftRegisters, VBlankTimeout), (frame_time_in_us - pre_vbi_time_in_us)); - polaris10_notify_smc_display_change(hwmgr, num_active_displays != 0); - return 0; } @@ -4821,7 +4888,7 @@ int polaris10_upload_mc_firmware(struct pp_hwmgr *hwmgr) return 0; } - data->need_long_memory_training = true; + data->need_long_memory_training = false; /* * PPMCME_FirmwareDescriptorEntry *pfd = NULL; diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.h index fd38b0d7a3c2..dbc6d9bfd5af 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.h +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.h @@ -311,6 +311,7 @@ struct polaris10_hwmgr { bool apply_optimized_settings; uint32_t avfs_vdroop_override_setting; bool apply_avfs_cks_off_voltage; + uint32_t frame_time_x2; }; /* To convert to Q8.8 format for firmware */ diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c index 5d70e2c47faf..a64db7ba4e0b 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c @@ -1256,7 +1256,7 @@ int atomctrl_set_ac_timing_ai(struct pp_hwmgr *hwmgr, uint32_t memory_clock, } int atomctrl_get_voltage_evv_on_sclk_ai(struct pp_hwmgr *hwmgr, uint8_t voltage_type, - uint32_t sclk, uint16_t virtual_voltage_Id, uint16_t *voltage) + uint32_t sclk, uint16_t virtual_voltage_Id, uint32_t *voltage) { int result; @@ -1274,7 +1274,7 @@ int atomctrl_get_voltage_evv_on_sclk_ai(struct pp_hwmgr *hwmgr, uint8_t voltage_ if (0 != result) return result; - *voltage = get_voltage_info_param_space.usVoltageLevel; + *voltage = ((GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_3 *)(&get_voltage_info_param_space))->ulVoltageLevel; return result; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.h b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.h index 248c5db5f380..1e35a9625baf 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.h +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.h @@ -305,7 +305,7 @@ extern int atomctrl_get_engine_pll_dividers_ai(struct pp_hwmgr *hwmgr, uint32_t extern int atomctrl_set_ac_timing_ai(struct pp_hwmgr *hwmgr, uint32_t memory_clock, uint8_t level); extern int atomctrl_get_voltage_evv_on_sclk_ai(struct pp_hwmgr *hwmgr, uint8_t voltage_type, - uint32_t sclk, uint16_t virtual_voltage_Id, uint16_t *voltage); + uint32_t sclk, uint16_t virtual_voltage_Id, uint32_t *voltage); extern int atomctrl_get_smc_sclk_range_table(struct pp_hwmgr *hwmgr, struct pp_atom_ctrl_sclk_range_table *table); extern int atomctrl_get_avfs_information(struct pp_hwmgr *hwmgr, struct pp_atom_ctrl__avfs_parameters *param); diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c index 6c4553cf8023..072b3b682637 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c @@ -1302,7 +1302,7 @@ static int tonga_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr, table->Smio[count] |= data->mvdd_voltage_table.entries[count].smio_low; } - table->SmioMask2 = data->vddci_voltage_table.mask_low; + table->SmioMask2 = data->mvdd_voltage_table.mask_low; CONVERT_FROM_HOST_TO_SMC_UL(table->MvddLevelCount); } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_processpptables.c b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_processpptables.c index 94d6b472e1fe..4662d3d0b7b1 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_processpptables.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_processpptables.c @@ -302,7 +302,7 @@ static int init_dpm_2_parameters( (((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usPPMTableOffset)); if (0 != powerplay_table->usPPMTableOffset) { - if (1 == get_platform_power_management_table(hwmgr, atom_ppm_table)) { + if (get_platform_power_management_table(hwmgr, atom_ppm_table) == 0) { phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_EnablePlatformPowerManagement); } diff --git a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h index 0bbc42a224e5..bf0d2accf7bf 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/powerplay/inc/hwmgr.h @@ -415,6 +415,8 @@ struct phm_cac_tdp_table { uint8_t ucVr_I2C_Line; uint8_t ucPlx_I2C_address; uint8_t ucPlx_I2C_Line; + uint32_t usBoostPowerLimit; + uint8_t ucCKS_LDO_REFSEL; }; struct phm_ppm_table { diff --git a/drivers/gpu/drm/amd/powerplay/inc/polaris10_ppsmc.h b/drivers/gpu/drm/amd/powerplay/inc/polaris10_ppsmc.h index d41d37ab5b7c..b8f4b73c322e 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/polaris10_ppsmc.h +++ b/drivers/gpu/drm/amd/powerplay/inc/polaris10_ppsmc.h @@ -392,6 +392,8 @@ typedef uint16_t PPSMC_Result; #define PPSMC_MSG_SetGpuPllDfsForSclk ((uint16_t) 0x300) #define PPSMC_MSG_Didt_Block_Function ((uint16_t) 0x301) +#define PPSMC_MSG_SetVBITimeout ((uint16_t) 0x306) + #define PPSMC_MSG_SecureSRBMWrite ((uint16_t) 0x600) #define PPSMC_MSG_SecureSRBMRead ((uint16_t) 0x601) #define PPSMC_MSG_SetAddress ((uint16_t) 0x800) diff --git a/drivers/gpu/drm/amd/powerplay/inc/smu74_discrete.h b/drivers/gpu/drm/amd/powerplay/inc/smu74_discrete.h index b85ff5400e57..899d6d8108c2 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/smu74_discrete.h +++ b/drivers/gpu/drm/amd/powerplay/inc/smu74_discrete.h @@ -270,7 +270,8 @@ struct SMU74_Discrete_DpmTable { uint8_t BootPhases; uint8_t VRHotLevel; - uint8_t Reserved1[3]; + uint8_t LdoRefSel; + uint8_t Reserved1[2]; uint16_t FanStartTemperature; uint16_t FanStopTemperature; uint16_t MaxVoltage; diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index a141921445f4..b590e678052d 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -58,6 +58,17 @@ config DRM_SII902X ---help--- Silicon Image sii902x bridge chip driver. +config DRM_TOSHIBA_TC358767 + tristate "Toshiba TC358767 eDP bridge" + depends on OF + select DRM_KMS_HELPER + select REGMAP_I2C + select DRM_PANEL + ---help--- + Toshiba TC358767 eDP bridge chip driver. + source "drivers/gpu/drm/bridge/analogix/Kconfig" +source "drivers/gpu/drm/bridge/adv7511/Kconfig" + endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index bfec9f8cb9d2..efdb07e878f5 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -6,4 +6,6 @@ obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o obj-$(CONFIG_DRM_SII902X) += sii902x.o +obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ +obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ diff --git a/drivers/gpu/drm/bridge/adv7511/Kconfig b/drivers/gpu/drm/bridge/adv7511/Kconfig new file mode 100644 index 000000000000..d2b0499ab7d7 --- /dev/null +++ b/drivers/gpu/drm/bridge/adv7511/Kconfig @@ -0,0 +1,15 @@ +config DRM_I2C_ADV7511 + tristate "AV7511 encoder" + depends on OF + select DRM_KMS_HELPER + select REGMAP_I2C + help + Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders. + +config DRM_I2C_ADV7533 + bool "ADV7533 encoder" + depends on DRM_I2C_ADV7511 + select DRM_MIPI_DSI + default y + help + Support for the Analog Devices ADV7533 DSI to HDMI encoder. diff --git a/drivers/gpu/drm/bridge/adv7511/Makefile b/drivers/gpu/drm/bridge/adv7511/Makefile new file mode 100644 index 000000000000..9019327fff4c --- /dev/null +++ b/drivers/gpu/drm/bridge/adv7511/Makefile @@ -0,0 +1,3 @@ +adv7511-y := adv7511_drv.o +adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o +obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o diff --git a/drivers/gpu/drm/i2c/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 38515b30cedf..161c923d6162 100644 --- a/drivers/gpu/drm/i2c/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -10,6 +10,11 @@ #define __DRM_I2C_ADV7511_H__ #include <linux/hdmi.h> +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include <drm/drm_crtc_helper.h> +#include <drm/drm_mipi_dsi.h> #define ADV7511_REG_CHIP_REVISION 0x00 #define ADV7511_REG_N0 0x01 @@ -286,4 +291,102 @@ struct adv7511_video_config { struct hdmi_avi_infoframe avi_infoframe; }; +enum adv7511_type { + ADV7511, + ADV7533, +}; + +struct adv7511 { + struct i2c_client *i2c_main; + struct i2c_client *i2c_edid; + struct i2c_client *i2c_cec; + + struct regmap *regmap; + struct regmap *regmap_cec; + enum drm_connector_status status; + bool powered; + + struct drm_display_mode curr_mode; + + unsigned int f_tmds; + + unsigned int current_edid_segment; + uint8_t edid_buf[256]; + bool edid_read; + + wait_queue_head_t wq; + struct drm_bridge bridge; + struct drm_connector connector; + + bool embedded_sync; + enum adv7511_sync_polarity vsync_polarity; + enum adv7511_sync_polarity hsync_polarity; + bool rgb; + + struct edid *edid; + + struct gpio_desc *gpio_pd; + + /* ADV7533 DSI RX related params */ + struct device_node *host_node; + struct mipi_dsi_device *dsi; + u8 num_dsi_lanes; + bool use_timing_gen; + + enum adv7511_type type; +}; + +#ifdef CONFIG_DRM_I2C_ADV7533 +void adv7533_dsi_power_on(struct adv7511 *adv); +void adv7533_dsi_power_off(struct adv7511 *adv); +void adv7533_mode_set(struct adv7511 *adv, struct drm_display_mode *mode); +int adv7533_patch_registers(struct adv7511 *adv); +void adv7533_uninit_cec(struct adv7511 *adv); +int adv7533_init_cec(struct adv7511 *adv); +int adv7533_attach_dsi(struct adv7511 *adv); +void adv7533_detach_dsi(struct adv7511 *adv); +int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv); +#else +static inline void adv7533_dsi_power_on(struct adv7511 *adv) +{ +} + +static inline void adv7533_dsi_power_off(struct adv7511 *adv) +{ +} + +static inline void adv7533_mode_set(struct adv7511 *adv, + struct drm_display_mode *mode) +{ +} + +static inline int adv7533_patch_registers(struct adv7511 *adv) +{ + return -ENODEV; +} + +static inline void adv7533_uninit_cec(struct adv7511 *adv) +{ +} + +static inline int adv7533_init_cec(struct adv7511 *adv) +{ + return -ENODEV; +} + +static inline int adv7533_attach_dsi(struct adv7511 *adv) +{ + return -ENODEV; +} + +static inline void adv7533_detach_dsi(struct adv7511 *adv) +{ +} + +static inline int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv) +{ + return -ENODEV; +} +#endif + #endif /* __DRM_I2C_ADV7511_H__ */ diff --git a/drivers/gpu/drm/i2c/adv7511.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index a02112ba1c3d..ec8fb2ed3275 100644 --- a/drivers/gpu/drm/i2c/adv7511.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -8,51 +8,17 @@ #include <linux/device.h> #include <linux/gpio/consumer.h> -#include <linux/i2c.h> #include <linux/module.h> -#include <linux/regmap.h> +#include <linux/of_device.h> #include <linux/slab.h> #include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_edid.h> -#include <drm/drm_encoder_slave.h> #include "adv7511.h" -struct adv7511 { - struct i2c_client *i2c_main; - struct i2c_client *i2c_edid; - - struct regmap *regmap; - struct regmap *packet_memory_regmap; - enum drm_connector_status status; - bool powered; - - unsigned int f_tmds; - - unsigned int current_edid_segment; - uint8_t edid_buf[256]; - bool edid_read; - - wait_queue_head_t wq; - struct drm_encoder *encoder; - - bool embedded_sync; - enum adv7511_sync_polarity vsync_polarity; - enum adv7511_sync_polarity hsync_polarity; - bool rgb; - - struct edid *edid; - - struct gpio_desc *gpio_pd; -}; - -static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder) -{ - return to_encoder_slave(encoder)->slave_priv; -} - /* ADI recommended values for proper operation. */ static const struct reg_sequence adv7511_fixed_registers[] = { { 0x98, 0x03 }, @@ -394,6 +360,9 @@ static void adv7511_power_on(struct adv7511 *adv7511) */ regcache_sync(adv7511->regmap); + if (adv7511->type == ADV7533) + adv7533_dsi_power_on(adv7511); + adv7511->powered = true; } @@ -405,6 +374,9 @@ static void adv7511_power_off(struct adv7511 *adv7511) ADV7511_POWER_POWER_DOWN); regcache_mark_dirty(adv7511->regmap); + if (adv7511->type == ADV7533) + adv7533_dsi_power_off(adv7511); + adv7511->powered = false; } @@ -430,7 +402,7 @@ static bool adv7511_hpd(struct adv7511 *adv7511) return false; } -static int adv7511_irq_process(struct adv7511 *adv7511) +static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) { unsigned int irq0, irq1; int ret; @@ -446,8 +418,8 @@ static int adv7511_irq_process(struct adv7511 *adv7511) regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0); regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1); - if (irq0 & ADV7511_INT0_HPD && adv7511->encoder) - drm_helper_hpd_irq_event(adv7511->encoder->dev); + if (process_hpd && irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder) + drm_helper_hpd_irq_event(adv7511->connector.dev); if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) { adv7511->edid_read = true; @@ -464,7 +436,7 @@ static irqreturn_t adv7511_irq_handler(int irq, void *devid) struct adv7511 *adv7511 = devid; int ret; - ret = adv7511_irq_process(adv7511); + ret = adv7511_irq_process(adv7511, true); return ret < 0 ? IRQ_NONE : IRQ_HANDLED; } @@ -481,7 +453,7 @@ static int adv7511_wait_for_edid(struct adv7511 *adv7511, int timeout) adv7511->edid_read, msecs_to_jiffies(timeout)); } else { for (; timeout > 0; timeout -= 25) { - ret = adv7511_irq_process(adv7511); + ret = adv7511_irq_process(adv7511, false); if (ret < 0) break; @@ -563,13 +535,12 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block, } /* ----------------------------------------------------------------------------- - * Encoder operations + * ADV75xx helpers */ -static int adv7511_get_modes(struct drm_encoder *encoder, +static int adv7511_get_modes(struct adv7511 *adv7511, struct drm_connector *connector) { - struct adv7511 *adv7511 = encoder_to_adv7511(encoder); struct edid *edid; unsigned int count; @@ -606,21 +577,9 @@ static int adv7511_get_modes(struct drm_encoder *encoder, return count; } -static void adv7511_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - struct adv7511 *adv7511 = encoder_to_adv7511(encoder); - - if (mode == DRM_MODE_DPMS_ON) - adv7511_power_on(adv7511); - else - adv7511_power_off(adv7511); -} - static enum drm_connector_status -adv7511_encoder_detect(struct drm_encoder *encoder, - struct drm_connector *connector) +adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector) { - struct adv7511 *adv7511 = encoder_to_adv7511(encoder); enum drm_connector_status status; unsigned int val; bool hpd; @@ -644,7 +603,7 @@ adv7511_encoder_detect(struct drm_encoder *encoder, if (status == connector_status_connected && hpd && adv7511->powered) { regcache_mark_dirty(adv7511->regmap); adv7511_power_on(adv7511); - adv7511_get_modes(encoder, connector); + adv7511_get_modes(adv7511, connector); if (adv7511->status == connector_status_connected) status = connector_status_disconnected; } else { @@ -658,8 +617,8 @@ adv7511_encoder_detect(struct drm_encoder *encoder, return status; } -static int adv7511_encoder_mode_valid(struct drm_encoder *encoder, - struct drm_display_mode *mode) +static int adv7511_mode_valid(struct adv7511 *adv7511, + struct drm_display_mode *mode) { if (mode->clock > 165000) return MODE_CLOCK_HIGH; @@ -667,11 +626,10 @@ static int adv7511_encoder_mode_valid(struct drm_encoder *encoder, return MODE_OK; } -static void adv7511_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adj_mode) +static void adv7511_mode_set(struct adv7511 *adv7511, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) { - struct adv7511 *adv7511 = encoder_to_adv7511(encoder); unsigned int low_refresh_rate; unsigned int hsync_polarity = 0; unsigned int vsync_polarity = 0; @@ -754,6 +712,11 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder, regmap_update_bits(adv7511->regmap, 0x17, 0x60, (vsync_polarity << 6) | (hsync_polarity << 5)); + if (adv7511->type == ADV7533) + adv7533_mode_set(adv7511, adj_mode); + + drm_mode_copy(&adv7511->curr_mode, adj_mode); + /* * TODO Test first order 4:2:2 to 4:4:4 up conversion method, which is * supposed to give better results. @@ -762,12 +725,114 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder, adv7511->f_tmds = mode->clock; } -static const struct drm_encoder_slave_funcs adv7511_encoder_funcs = { - .dpms = adv7511_encoder_dpms, - .mode_valid = adv7511_encoder_mode_valid, - .mode_set = adv7511_encoder_mode_set, - .detect = adv7511_encoder_detect, - .get_modes = adv7511_get_modes, +/* Connector funcs */ +static struct adv7511 *connector_to_adv7511(struct drm_connector *connector) +{ + return container_of(connector, struct adv7511, connector); +} + +static int adv7511_connector_get_modes(struct drm_connector *connector) +{ + struct adv7511 *adv = connector_to_adv7511(connector); + + return adv7511_get_modes(adv, connector); +} + +static enum drm_mode_status +adv7511_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct adv7511 *adv = connector_to_adv7511(connector); + + return adv7511_mode_valid(adv, mode); +} + +static struct drm_connector_helper_funcs adv7511_connector_helper_funcs = { + .get_modes = adv7511_connector_get_modes, + .mode_valid = adv7511_connector_mode_valid, +}; + +static enum drm_connector_status +adv7511_connector_detect(struct drm_connector *connector, bool force) +{ + struct adv7511 *adv = connector_to_adv7511(connector); + + return adv7511_detect(adv, connector); +} + +static struct drm_connector_funcs adv7511_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = adv7511_connector_detect, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +/* Bridge funcs */ +static struct adv7511 *bridge_to_adv7511(struct drm_bridge *bridge) +{ + return container_of(bridge, struct adv7511, bridge); +} + +static void adv7511_bridge_enable(struct drm_bridge *bridge) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + + adv7511_power_on(adv); +} + +static void adv7511_bridge_disable(struct drm_bridge *bridge) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + + adv7511_power_off(adv); +} + +static void adv7511_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + + adv7511_mode_set(adv, mode, adj_mode); +} + +static int adv7511_bridge_attach(struct drm_bridge *bridge) +{ + struct adv7511 *adv = bridge_to_adv7511(bridge); + int ret; + + if (!bridge->encoder) { + DRM_ERROR("Parent encoder object not found"); + return -ENODEV; + } + + adv->connector.polled = DRM_CONNECTOR_POLL_HPD; + + ret = drm_connector_init(bridge->dev, &adv->connector, + &adv7511_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + drm_connector_helper_add(&adv->connector, + &adv7511_connector_helper_funcs); + drm_mode_connector_attach_encoder(&adv->connector, bridge->encoder); + + if (adv->type == ADV7533) + ret = adv7533_attach_dsi(adv); + + return ret; +} + +static struct drm_bridge_funcs adv7511_bridge_funcs = { + .enable = adv7511_bridge_enable, + .disable = adv7511_bridge_disable, + .mode_set = adv7511_bridge_mode_set, + .attach = adv7511_bridge_attach, }; /* ----------------------------------------------------------------------------- @@ -780,8 +845,6 @@ static int adv7511_parse_dt(struct device_node *np, const char *str; int ret; - memset(config, 0, sizeof(*config)); - of_property_read_u32(np, "adi,input-depth", &config->input_color_depth); if (config->input_color_depth != 8 && config->input_color_depth != 10 && config->input_color_depth != 12) @@ -881,7 +944,17 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) adv7511->powered = false; adv7511->status = connector_status_disconnected; - ret = adv7511_parse_dt(dev->of_node, &link_config); + if (dev->of_node) + adv7511->type = (enum adv7511_type)of_device_get_match_data(dev); + else + adv7511->type = id->driver_data; + + memset(&link_config, 0, sizeof(link_config)); + + if (adv7511->type == ADV7511) + ret = adv7511_parse_dt(dev->of_node, &link_config); + else + ret = adv7533_parse_dt(dev->of_node, adv7511); if (ret) return ret; @@ -907,8 +980,12 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) return ret; dev_dbg(dev, "Rev. %d\n", val); - ret = regmap_register_patch(adv7511->regmap, adv7511_fixed_registers, - ARRAY_SIZE(adv7511_fixed_registers)); + if (adv7511->type == ADV7511) + ret = regmap_register_patch(adv7511->regmap, + adv7511_fixed_registers, + ARRAY_SIZE(adv7511_fixed_registers)); + else + ret = adv7533_patch_registers(adv7511); if (ret) return ret; @@ -923,6 +1000,12 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) if (!adv7511->i2c_edid) return -ENOMEM; + if (adv7511->type == ADV7533) { + ret = adv7533_init_cec(adv7511); + if (ret) + goto err_i2c_unregister_edid; + } + if (i2c->irq) { init_waitqueue_head(&adv7511->wq); @@ -931,7 +1014,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) IRQF_ONESHOT, dev_name(dev), adv7511); if (ret) - goto err_i2c_unregister_device; + goto err_unregister_cec; } /* CEC is unused for now */ @@ -942,11 +1025,23 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) i2c_set_clientdata(i2c, adv7511); - adv7511_set_link_config(adv7511, &link_config); + if (adv7511->type == ADV7511) + adv7511_set_link_config(adv7511, &link_config); + + adv7511->bridge.funcs = &adv7511_bridge_funcs; + adv7511->bridge.of_node = dev->of_node; + + ret = drm_bridge_add(&adv7511->bridge); + if (ret) { + dev_err(dev, "failed to add adv7511 bridge\n"); + goto err_unregister_cec; + } return 0; -err_i2c_unregister_device: +err_unregister_cec: + adv7533_uninit_cec(adv7511); +err_i2c_unregister_edid: i2c_unregister_device(adv7511->i2c_edid); return ret; @@ -956,66 +1051,71 @@ static int adv7511_remove(struct i2c_client *i2c) { struct adv7511 *adv7511 = i2c_get_clientdata(i2c); - i2c_unregister_device(adv7511->i2c_edid); - - kfree(adv7511->edid); - - return 0; -} - -static int adv7511_encoder_init(struct i2c_client *i2c, struct drm_device *dev, - struct drm_encoder_slave *encoder) -{ + if (adv7511->type == ADV7533) { + adv7533_detach_dsi(adv7511); + adv7533_uninit_cec(adv7511); + } - struct adv7511 *adv7511 = i2c_get_clientdata(i2c); + drm_bridge_remove(&adv7511->bridge); - encoder->slave_priv = adv7511; - encoder->slave_funcs = &adv7511_encoder_funcs; + i2c_unregister_device(adv7511->i2c_edid); - adv7511->encoder = &encoder->base; + kfree(adv7511->edid); return 0; } static const struct i2c_device_id adv7511_i2c_ids[] = { - { "adv7511", 0 }, - { "adv7511w", 0 }, - { "adv7513", 0 }, + { "adv7511", ADV7511 }, + { "adv7511w", ADV7511 }, + { "adv7513", ADV7511 }, +#ifdef CONFIG_DRM_I2C_ADV7533 + { "adv7533", ADV7533 }, +#endif { } }; MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids); static const struct of_device_id adv7511_of_ids[] = { - { .compatible = "adi,adv7511", }, - { .compatible = "adi,adv7511w", }, - { .compatible = "adi,adv7513", }, + { .compatible = "adi,adv7511", .data = (void *)ADV7511 }, + { .compatible = "adi,adv7511w", .data = (void *)ADV7511 }, + { .compatible = "adi,adv7513", .data = (void *)ADV7511 }, +#ifdef CONFIG_DRM_I2C_ADV7533 + { .compatible = "adi,adv7533", .data = (void *)ADV7533 }, +#endif { } }; MODULE_DEVICE_TABLE(of, adv7511_of_ids); -static struct drm_i2c_encoder_driver adv7511_driver = { - .i2c_driver = { - .driver = { - .name = "adv7511", - .of_match_table = adv7511_of_ids, - }, - .id_table = adv7511_i2c_ids, - .probe = adv7511_probe, - .remove = adv7511_remove, - }, +static struct mipi_dsi_driver adv7533_dsi_driver = { + .driver.name = "adv7533", +}; - .encoder_init = adv7511_encoder_init, +static struct i2c_driver adv7511_driver = { + .driver = { + .name = "adv7511", + .of_match_table = adv7511_of_ids, + }, + .id_table = adv7511_i2c_ids, + .probe = adv7511_probe, + .remove = adv7511_remove, }; static int __init adv7511_init(void) { - return drm_i2c_encoder_register(THIS_MODULE, &adv7511_driver); + if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) + mipi_dsi_driver_register(&adv7533_dsi_driver); + + return i2c_add_driver(&adv7511_driver); } module_init(adv7511_init); static void __exit adv7511_exit(void) { - drm_i2c_encoder_unregister(&adv7511_driver); + i2c_del_driver(&adv7511_driver); + + if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) + mipi_dsi_driver_unregister(&adv7533_dsi_driver); } module_exit(adv7511_exit); diff --git a/drivers/gpu/drm/bridge/adv7511/adv7533.c b/drivers/gpu/drm/bridge/adv7511/adv7533.c new file mode 100644 index 000000000000..5eebd15899b1 --- /dev/null +++ b/drivers/gpu/drm/bridge/adv7511/adv7533.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/of_graph.h> + +#include "adv7511.h" + +static const struct reg_sequence adv7533_fixed_registers[] = { + { 0x16, 0x20 }, + { 0x9a, 0xe0 }, + { 0xba, 0x70 }, + { 0xde, 0x82 }, + { 0xe4, 0x40 }, + { 0xe5, 0x80 }, +}; + +static const struct reg_sequence adv7533_cec_fixed_registers[] = { + { 0x15, 0xd0 }, + { 0x17, 0xd0 }, + { 0x24, 0x20 }, + { 0x57, 0x11 }, +}; + +static const struct regmap_config adv7533_cec_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_RBTREE, +}; + +static void adv7511_dsi_config_timing_gen(struct adv7511 *adv) +{ + struct mipi_dsi_device *dsi = adv->dsi; + struct drm_display_mode *mode = &adv->curr_mode; + unsigned int hsw, hfp, hbp, vsw, vfp, vbp; + u8 clock_div_by_lanes[] = { 6, 4, 3 }; /* 2, 3, 4 lanes */ + + hsw = mode->hsync_end - mode->hsync_start; + hfp = mode->hsync_start - mode->hdisplay; + hbp = mode->htotal - mode->hsync_end; + vsw = mode->vsync_end - mode->vsync_start; + vfp = mode->vsync_start - mode->vdisplay; + vbp = mode->vtotal - mode->vsync_end; + + /* set pixel clock divider mode */ + regmap_write(adv->regmap_cec, 0x16, + clock_div_by_lanes[dsi->lanes - 2] << 3); + + /* horizontal porch params */ + regmap_write(adv->regmap_cec, 0x28, mode->htotal >> 4); + regmap_write(adv->regmap_cec, 0x29, (mode->htotal << 4) & 0xff); + regmap_write(adv->regmap_cec, 0x2a, hsw >> 4); + regmap_write(adv->regmap_cec, 0x2b, (hsw << 4) & 0xff); + regmap_write(adv->regmap_cec, 0x2c, hfp >> 4); + regmap_write(adv->regmap_cec, 0x2d, (hfp << 4) & 0xff); + regmap_write(adv->regmap_cec, 0x2e, hbp >> 4); + regmap_write(adv->regmap_cec, 0x2f, (hbp << 4) & 0xff); + + /* vertical porch params */ + regmap_write(adv->regmap_cec, 0x30, mode->vtotal >> 4); + regmap_write(adv->regmap_cec, 0x31, (mode->vtotal << 4) & 0xff); + regmap_write(adv->regmap_cec, 0x32, vsw >> 4); + regmap_write(adv->regmap_cec, 0x33, (vsw << 4) & 0xff); + regmap_write(adv->regmap_cec, 0x34, vfp >> 4); + regmap_write(adv->regmap_cec, 0x35, (vfp << 4) & 0xff); + regmap_write(adv->regmap_cec, 0x36, vbp >> 4); + regmap_write(adv->regmap_cec, 0x37, (vbp << 4) & 0xff); +} + +void adv7533_dsi_power_on(struct adv7511 *adv) +{ + struct mipi_dsi_device *dsi = adv->dsi; + + if (adv->use_timing_gen) + adv7511_dsi_config_timing_gen(adv); + + /* set number of dsi lanes */ + regmap_write(adv->regmap_cec, 0x1c, dsi->lanes << 4); + + if (adv->use_timing_gen) { + /* reset internal timing generator */ + regmap_write(adv->regmap_cec, 0x27, 0xcb); + regmap_write(adv->regmap_cec, 0x27, 0x8b); + regmap_write(adv->regmap_cec, 0x27, 0xcb); + } else { + /* disable internal timing generator */ + regmap_write(adv->regmap_cec, 0x27, 0x0b); + } + + /* enable hdmi */ + regmap_write(adv->regmap_cec, 0x03, 0x89); + /* disable test mode */ + regmap_write(adv->regmap_cec, 0x55, 0x00); + + regmap_register_patch(adv->regmap_cec, adv7533_cec_fixed_registers, + ARRAY_SIZE(adv7533_cec_fixed_registers)); +} + +void adv7533_dsi_power_off(struct adv7511 *adv) +{ + /* disable hdmi */ + regmap_write(adv->regmap_cec, 0x03, 0x0b); + /* disable internal timing generator */ + regmap_write(adv->regmap_cec, 0x27, 0x0b); +} + +void adv7533_mode_set(struct adv7511 *adv, struct drm_display_mode *mode) +{ + struct mipi_dsi_device *dsi = adv->dsi; + int lanes, ret; + + if (adv->num_dsi_lanes != 4) + return; + + if (mode->clock > 80000) + lanes = 4; + else + lanes = 3; + + if (lanes != dsi->lanes) { + mipi_dsi_detach(dsi); + dsi->lanes = lanes; + ret = mipi_dsi_attach(dsi); + if (ret) + dev_err(&dsi->dev, "failed to change host lanes\n"); + } +} + +int adv7533_patch_registers(struct adv7511 *adv) +{ + return regmap_register_patch(adv->regmap, + adv7533_fixed_registers, + ARRAY_SIZE(adv7533_fixed_registers)); +} + +void adv7533_uninit_cec(struct adv7511 *adv) +{ + i2c_unregister_device(adv->i2c_cec); +} + +static const int cec_i2c_addr = 0x78; + +int adv7533_init_cec(struct adv7511 *adv) +{ + int ret; + + adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter, cec_i2c_addr >> 1); + if (!adv->i2c_cec) + return -ENOMEM; + + adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec, + &adv7533_cec_regmap_config); + if (IS_ERR(adv->regmap_cec)) { + ret = PTR_ERR(adv->regmap_cec); + goto err; + } + + ret = regmap_register_patch(adv->regmap_cec, + adv7533_cec_fixed_registers, + ARRAY_SIZE(adv7533_cec_fixed_registers)); + if (ret) + goto err; + + return 0; +err: + adv7533_uninit_cec(adv); + return ret; +} + +int adv7533_attach_dsi(struct adv7511 *adv) +{ + struct device *dev = &adv->i2c_main->dev; + struct mipi_dsi_host *host; + struct mipi_dsi_device *dsi; + int ret = 0; + const struct mipi_dsi_device_info info = { .type = "adv7533", + .channel = 0, + .node = NULL, + }; + + host = of_find_mipi_dsi_host_by_node(adv->host_node); + if (!host) { + dev_err(dev, "failed to find dsi host\n"); + return -EPROBE_DEFER; + } + + dsi = mipi_dsi_device_register_full(host, &info); + if (IS_ERR(dsi)) { + dev_err(dev, "failed to create dsi device\n"); + ret = PTR_ERR(dsi); + goto err_dsi_device; + } + + adv->dsi = dsi; + + dsi->lanes = adv->num_dsi_lanes; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "failed to attach dsi to host\n"); + goto err_dsi_attach; + } + + return 0; + +err_dsi_attach: + mipi_dsi_device_unregister(dsi); +err_dsi_device: + return ret; +} + +void adv7533_detach_dsi(struct adv7511 *adv) +{ + mipi_dsi_detach(adv->dsi); + mipi_dsi_device_unregister(adv->dsi); +} + +int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv) +{ + u32 num_lanes; + struct device_node *endpoint; + + of_property_read_u32(np, "adi,dsi-lanes", &num_lanes); + + if (num_lanes < 1 || num_lanes > 4) + return -EINVAL; + + adv->num_dsi_lanes = num_lanes; + + endpoint = of_graph_get_next_endpoint(np, NULL); + if (!endpoint) + return -ENODEV; + + adv->host_node = of_graph_get_remote_port_parent(endpoint); + if (!adv->host_node) { + of_node_put(endpoint); + return -ENODEV; + } + + of_node_put(endpoint); + of_node_put(adv->host_node); + + adv->use_timing_gen = !of_property_read_bool(np, + "adi,disable-timing-generator"); + + /* TODO: Check if these need to be parsed by DT or not */ + adv->rgb = true; + adv->embedded_sync = false; + + return 0; +} diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c new file mode 100644 index 000000000000..a09825d8c94a --- /dev/null +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -0,0 +1,1413 @@ +/* + * tc358767 eDP bridge driver + * + * Copyright (C) 2016 CogentEmbedded Inc + * Author: Andrey Gusakov <andrey.gusakov@cogentembedded.com> + * + * Copyright (C) 2016 Pengutronix, Philipp Zabel <p.zabel@pengutronix.de> + * + * Initially based on: drivers/gpu/drm/i2c/tda998x_drv.c + * + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_dp_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> + +/* Registers */ + +/* Display Parallel Interface */ +#define DPIPXLFMT 0x0440 +#define VS_POL_ACTIVE_LOW (1 << 10) +#define HS_POL_ACTIVE_LOW (1 << 9) +#define DE_POL_ACTIVE_HIGH (0 << 8) +#define SUB_CFG_TYPE_CONFIG1 (0 << 2) /* LSB aligned */ +#define SUB_CFG_TYPE_CONFIG2 (1 << 2) /* Loosely Packed */ +#define SUB_CFG_TYPE_CONFIG3 (2 << 2) /* LSB aligned 8-bit */ +#define DPI_BPP_RGB888 (0 << 0) +#define DPI_BPP_RGB666 (1 << 0) +#define DPI_BPP_RGB565 (2 << 0) + +/* Video Path */ +#define VPCTRL0 0x0450 +#define OPXLFMT_RGB666 (0 << 8) +#define OPXLFMT_RGB888 (1 << 8) +#define FRMSYNC_DISABLED (0 << 4) /* Video Timing Gen Disabled */ +#define FRMSYNC_ENABLED (1 << 4) /* Video Timing Gen Enabled */ +#define MSF_DISABLED (0 << 0) /* Magic Square FRC disabled */ +#define MSF_ENABLED (1 << 0) /* Magic Square FRC enabled */ +#define HTIM01 0x0454 +#define HTIM02 0x0458 +#define VTIM01 0x045c +#define VTIM02 0x0460 +#define VFUEN0 0x0464 +#define VFUEN BIT(0) /* Video Frame Timing Upload */ + +/* System */ +#define TC_IDREG 0x0500 +#define SYSCTRL 0x0510 +#define DP0_AUDSRC_NO_INPUT (0 << 3) +#define DP0_AUDSRC_I2S_RX (1 << 3) +#define DP0_VIDSRC_NO_INPUT (0 << 0) +#define DP0_VIDSRC_DSI_RX (1 << 0) +#define DP0_VIDSRC_DPI_RX (2 << 0) +#define DP0_VIDSRC_COLOR_BAR (3 << 0) + +/* Control */ +#define DP0CTL 0x0600 +#define VID_MN_GEN BIT(6) /* Auto-generate M/N values */ +#define EF_EN BIT(5) /* Enable Enhanced Framing */ +#define VID_EN BIT(1) /* Video transmission enable */ +#define DP_EN BIT(0) /* Enable DPTX function */ + +/* Clocks */ +#define DP0_VIDMNGEN0 0x0610 +#define DP0_VIDMNGEN1 0x0614 +#define DP0_VMNGENSTATUS 0x0618 + +/* Main Channel */ +#define DP0_SECSAMPLE 0x0640 +#define DP0_VIDSYNCDELAY 0x0644 +#define DP0_TOTALVAL 0x0648 +#define DP0_STARTVAL 0x064c +#define DP0_ACTIVEVAL 0x0650 +#define DP0_SYNCVAL 0x0654 +#define DP0_MISC 0x0658 +#define TU_SIZE_RECOMMENDED (0x3f << 16) /* LSCLK cycles per TU */ +#define BPC_6 (0 << 5) +#define BPC_8 (1 << 5) + +/* AUX channel */ +#define DP0_AUXCFG0 0x0660 +#define DP0_AUXCFG1 0x0664 +#define AUX_RX_FILTER_EN BIT(16) + +#define DP0_AUXADDR 0x0668 +#define DP0_AUXWDATA(i) (0x066c + (i) * 4) +#define DP0_AUXRDATA(i) (0x067c + (i) * 4) +#define DP0_AUXSTATUS 0x068c +#define AUX_STATUS_MASK 0xf0 +#define AUX_STATUS_SHIFT 4 +#define AUX_TIMEOUT BIT(1) +#define AUX_BUSY BIT(0) +#define DP0_AUXI2CADR 0x0698 + +/* Link Training */ +#define DP0_SRCCTRL 0x06a0 +#define DP0_SRCCTRL_SCRMBLDIS BIT(13) +#define DP0_SRCCTRL_EN810B BIT(12) +#define DP0_SRCCTRL_NOTP (0 << 8) +#define DP0_SRCCTRL_TP1 (1 << 8) +#define DP0_SRCCTRL_TP2 (2 << 8) +#define DP0_SRCCTRL_LANESKEW BIT(7) +#define DP0_SRCCTRL_SSCG BIT(3) +#define DP0_SRCCTRL_LANES_1 (0 << 2) +#define DP0_SRCCTRL_LANES_2 (1 << 2) +#define DP0_SRCCTRL_BW27 (1 << 1) +#define DP0_SRCCTRL_BW162 (0 << 1) +#define DP0_SRCCTRL_AUTOCORRECT BIT(0) +#define DP0_LTSTAT 0x06d0 +#define LT_LOOPDONE BIT(13) +#define LT_STATUS_MASK (0x1f << 8) +#define LT_CHANNEL1_EQ_BITS (DP_CHANNEL_EQ_BITS << 4) +#define LT_INTERLANE_ALIGN_DONE BIT(3) +#define LT_CHANNEL0_EQ_BITS (DP_CHANNEL_EQ_BITS) +#define DP0_SNKLTCHGREQ 0x06d4 +#define DP0_LTLOOPCTRL 0x06d8 +#define DP0_SNKLTCTRL 0x06e4 + +/* PHY */ +#define DP_PHY_CTRL 0x0800 +#define DP_PHY_RST BIT(28) /* DP PHY Global Soft Reset */ +#define BGREN BIT(25) /* AUX PHY BGR Enable */ +#define PWR_SW_EN BIT(24) /* PHY Power Switch Enable */ +#define PHY_M1_RST BIT(12) /* Reset PHY1 Main Channel */ +#define PHY_RDY BIT(16) /* PHY Main Channels Ready */ +#define PHY_M0_RST BIT(8) /* Reset PHY0 Main Channel */ +#define PHY_A0_EN BIT(1) /* PHY Aux Channel0 Enable */ +#define PHY_M0_EN BIT(0) /* PHY Main Channel0 Enable */ + +/* PLL */ +#define DP0_PLLCTRL 0x0900 +#define DP1_PLLCTRL 0x0904 /* not defined in DS */ +#define PXL_PLLCTRL 0x0908 +#define PLLUPDATE BIT(2) +#define PLLBYP BIT(1) +#define PLLEN BIT(0) +#define PXL_PLLPARAM 0x0914 +#define IN_SEL_REFCLK (0 << 14) +#define SYS_PLLPARAM 0x0918 +#define REF_FREQ_38M4 (0 << 8) /* 38.4 MHz */ +#define REF_FREQ_19M2 (1 << 8) /* 19.2 MHz */ +#define REF_FREQ_26M (2 << 8) /* 26 MHz */ +#define REF_FREQ_13M (3 << 8) /* 13 MHz */ +#define SYSCLK_SEL_LSCLK (0 << 4) +#define LSCLK_DIV_1 (0 << 0) +#define LSCLK_DIV_2 (1 << 0) + +/* Test & Debug */ +#define TSTCTL 0x0a00 +#define PLL_DBG 0x0a04 + +static bool tc_test_pattern; +module_param_named(test, tc_test_pattern, bool, 0644); + +struct tc_edp_link { + struct drm_dp_link base; + u8 assr; + int scrambler_dis; + int spread; + int coding8b10b; + u8 swing; + u8 preemp; +}; + +struct tc_data { + struct device *dev; + struct regmap *regmap; + struct drm_dp_aux aux; + + struct drm_bridge bridge; + struct drm_connector connector; + struct drm_panel *panel; + + /* link settings */ + struct tc_edp_link link; + + /* display edid */ + struct edid *edid; + /* current mode */ + struct drm_display_mode *mode; + + u32 rev; + u8 assr; + + struct gpio_desc *sd_gpio; + struct gpio_desc *reset_gpio; + struct clk *refclk; +}; + +static inline struct tc_data *aux_to_tc(struct drm_dp_aux *a) +{ + return container_of(a, struct tc_data, aux); +} + +static inline struct tc_data *bridge_to_tc(struct drm_bridge *b) +{ + return container_of(b, struct tc_data, bridge); +} + +static inline struct tc_data *connector_to_tc(struct drm_connector *c) +{ + return container_of(c, struct tc_data, connector); +} + +/* Simple macros to avoid repeated error checks */ +#define tc_write(reg, var) \ + do { \ + ret = regmap_write(tc->regmap, reg, var); \ + if (ret) \ + goto err; \ + } while (0) +#define tc_read(reg, var) \ + do { \ + ret = regmap_read(tc->regmap, reg, var); \ + if (ret) \ + goto err; \ + } while (0) + +static inline int tc_poll_timeout(struct regmap *map, unsigned int addr, + unsigned int cond_mask, + unsigned int cond_value, + unsigned long sleep_us, u64 timeout_us) +{ + ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); + unsigned int val; + int ret; + + for (;;) { + ret = regmap_read(map, addr, &val); + if (ret) + break; + if ((val & cond_mask) == cond_value) + break; + if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { + ret = regmap_read(map, addr, &val); + break; + } + if (sleep_us) + usleep_range((sleep_us >> 2) + 1, sleep_us); + } + return ret ?: (((val & cond_mask) == cond_value) ? 0 : -ETIMEDOUT); +} + +static int tc_aux_wait_busy(struct tc_data *tc, unsigned int timeout_ms) +{ + return tc_poll_timeout(tc->regmap, DP0_AUXSTATUS, AUX_BUSY, 0, + 1000, 1000 * timeout_ms); +} + +static int tc_aux_get_status(struct tc_data *tc, u8 *reply) +{ + int ret; + u32 value; + + ret = regmap_read(tc->regmap, DP0_AUXSTATUS, &value); + if (ret < 0) + return ret; + if (value & AUX_BUSY) { + if (value & AUX_TIMEOUT) { + dev_err(tc->dev, "i2c access timeout!\n"); + return -ETIMEDOUT; + } + return -EBUSY; + } + + *reply = (value & AUX_STATUS_MASK) >> AUX_STATUS_SHIFT; + return 0; +} + +static ssize_t tc_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct tc_data *tc = aux_to_tc(aux); + size_t size = min_t(size_t, 8, msg->size); + u8 request = msg->request & ~DP_AUX_I2C_MOT; + u8 *buf = msg->buffer; + u32 tmp = 0; + int i = 0; + int ret; + + if (size == 0) + return 0; + + ret = tc_aux_wait_busy(tc, 100); + if (ret) + goto err; + + if (request == DP_AUX_I2C_WRITE || request == DP_AUX_NATIVE_WRITE) { + /* Store data */ + while (i < size) { + if (request == DP_AUX_NATIVE_WRITE) + tmp = tmp | (buf[i] << (8 * (i & 0x3))); + else + tmp = (tmp << 8) | buf[i]; + i++; + if (((i % 4) == 0) || (i == size)) { + tc_write(DP0_AUXWDATA(i >> 2), tmp); + tmp = 0; + } + } + } else if (request != DP_AUX_I2C_READ && + request != DP_AUX_NATIVE_READ) { + return -EINVAL; + } + + /* Store address */ + tc_write(DP0_AUXADDR, msg->address); + /* Start transfer */ + tc_write(DP0_AUXCFG0, ((size - 1) << 8) | request); + + ret = tc_aux_wait_busy(tc, 100); + if (ret) + goto err; + + ret = tc_aux_get_status(tc, &msg->reply); + if (ret) + goto err; + + if (request == DP_AUX_I2C_READ || request == DP_AUX_NATIVE_READ) { + /* Read data */ + while (i < size) { + if ((i % 4) == 0) + tc_read(DP0_AUXRDATA(i >> 2), &tmp); + buf[i] = tmp & 0xff; + tmp = tmp >> 8; + i++; + } + } + + return size; +err: + return ret; +} + +static const char * const training_pattern1_errors[] = { + "No errors", + "Aux write error", + "Aux read error", + "Max voltage reached error", + "Loop counter expired error", + "res", "res", "res" +}; + +static const char * const training_pattern2_errors[] = { + "No errors", + "Aux write error", + "Aux read error", + "Clock recovery failed error", + "Loop counter expired error", + "res", "res", "res" +}; + +static u32 tc_srcctrl(struct tc_data *tc) +{ + /* + * No training pattern, skew lane 1 data by two LSCLK cycles with + * respect to lane 0 data, AutoCorrect Mode = 0 + */ + u32 reg = DP0_SRCCTRL_NOTP | DP0_SRCCTRL_LANESKEW; + + if (tc->link.scrambler_dis) + reg |= DP0_SRCCTRL_SCRMBLDIS; /* Scrambler Disabled */ + if (tc->link.coding8b10b) + /* Enable 8/10B Encoder (TxData[19:16] not used) */ + reg |= DP0_SRCCTRL_EN810B; + if (tc->link.spread) + reg |= DP0_SRCCTRL_SSCG; /* Spread Spectrum Enable */ + if (tc->link.base.num_lanes == 2) + reg |= DP0_SRCCTRL_LANES_2; /* Two Main Channel Lanes */ + if (tc->link.base.rate != 162000) + reg |= DP0_SRCCTRL_BW27; /* 2.7 Gbps link */ + return reg; +} + +static void tc_wait_pll_lock(struct tc_data *tc) +{ + /* Wait for PLL to lock: up to 2.09 ms, depending on refclk */ + usleep_range(3000, 6000); +} + +static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock) +{ + int ret; + int i_pre, best_pre = 1; + int i_post, best_post = 1; + int div, best_div = 1; + int mul, best_mul = 1; + int delta, best_delta; + int ext_div[] = {1, 2, 3, 5, 7}; + int best_pixelclock = 0; + int vco_hi = 0; + + dev_dbg(tc->dev, "PLL: requested %d pixelclock, ref %d\n", pixelclock, + refclk); + best_delta = pixelclock; + /* Loop over all possible ext_divs, skipping invalid configurations */ + for (i_pre = 0; i_pre < ARRAY_SIZE(ext_div); i_pre++) { + /* + * refclk / ext_pre_div should be in the 1 to 200 MHz range. + * We don't allow any refclk > 200 MHz, only check lower bounds. + */ + if (refclk / ext_div[i_pre] < 1000000) + continue; + for (i_post = 0; i_post < ARRAY_SIZE(ext_div); i_post++) { + for (div = 1; div <= 16; div++) { + u32 clk; + u64 tmp; + + tmp = pixelclock * ext_div[i_pre] * + ext_div[i_post] * div; + do_div(tmp, refclk); + mul = tmp; + + /* Check limits */ + if ((mul < 1) || (mul > 128)) + continue; + + clk = (refclk / ext_div[i_pre] / div) * mul; + /* + * refclk * mul / (ext_pre_div * pre_div) + * should be in the 150 to 650 MHz range + */ + if ((clk > 650000000) || (clk < 150000000)) + continue; + + clk = clk / ext_div[i_post]; + delta = clk - pixelclock; + + if (abs(delta) < abs(best_delta)) { + best_pre = i_pre; + best_post = i_post; + best_div = div; + best_mul = mul; + best_delta = delta; + best_pixelclock = clk; + } + } + } + } + if (best_pixelclock == 0) { + dev_err(tc->dev, "Failed to calc clock for %d pixelclock\n", + pixelclock); + return -EINVAL; + } + + dev_dbg(tc->dev, "PLL: got %d, delta %d\n", best_pixelclock, + best_delta); + dev_dbg(tc->dev, "PLL: %d / %d / %d * %d / %d\n", refclk, + ext_div[best_pre], best_div, best_mul, ext_div[best_post]); + + /* if VCO >= 300 MHz */ + if (refclk / ext_div[best_pre] / best_div * best_mul >= 300000000) + vco_hi = 1; + /* see DS */ + if (best_div == 16) + best_div = 0; + if (best_mul == 128) + best_mul = 0; + + /* Power up PLL and switch to bypass */ + tc_write(PXL_PLLCTRL, PLLBYP | PLLEN); + + tc_write(PXL_PLLPARAM, + (vco_hi << 24) | /* For PLL VCO >= 300 MHz = 1 */ + (ext_div[best_pre] << 20) | /* External Pre-divider */ + (ext_div[best_post] << 16) | /* External Post-divider */ + IN_SEL_REFCLK | /* Use RefClk as PLL input */ + (best_div << 8) | /* Divider for PLL RefClk */ + (best_mul << 0)); /* Multiplier for PLL */ + + /* Force PLL parameter update and disable bypass */ + tc_write(PXL_PLLCTRL, PLLUPDATE | PLLEN); + + tc_wait_pll_lock(tc); + + return 0; +err: + return ret; +} + +static int tc_pxl_pll_dis(struct tc_data *tc) +{ + /* Enable PLL bypass, power down PLL */ + return regmap_write(tc->regmap, PXL_PLLCTRL, PLLBYP); +} + +static int tc_stream_clock_calc(struct tc_data *tc) +{ + int ret; + /* + * If the Stream clock and Link Symbol clock are + * asynchronous with each other, the value of M changes over + * time. This way of generating link clock and stream + * clock is called Asynchronous Clock mode. The value M + * must change while the value N stays constant. The + * value of N in this Asynchronous Clock mode must be set + * to 2^15 or 32,768. + * + * LSCLK = 1/10 of high speed link clock + * + * f_STRMCLK = M/N * f_LSCLK + * M/N = f_STRMCLK / f_LSCLK + * + */ + tc_write(DP0_VIDMNGEN1, 32768); + + return 0; +err: + return ret; +} + +static int tc_aux_link_setup(struct tc_data *tc) +{ + unsigned long rate; + u32 value; + int ret; + + rate = clk_get_rate(tc->refclk); + switch (rate) { + case 38400000: + value = REF_FREQ_38M4; + break; + case 26000000: + value = REF_FREQ_26M; + break; + case 19200000: + value = REF_FREQ_19M2; + break; + case 13000000: + value = REF_FREQ_13M; + break; + default: + dev_err(tc->dev, "Invalid refclk rate: %lu Hz\n", rate); + return -EINVAL; + } + + /* Setup DP-PHY / PLL */ + value |= SYSCLK_SEL_LSCLK | LSCLK_DIV_2; + tc_write(SYS_PLLPARAM, value); + + tc_write(DP_PHY_CTRL, BGREN | PWR_SW_EN | BIT(2) | PHY_A0_EN); + + /* + * Initially PLLs are in bypass. Force PLL parameter update, + * disable PLL bypass, enable PLL + */ + tc_write(DP0_PLLCTRL, PLLUPDATE | PLLEN); + tc_wait_pll_lock(tc); + + tc_write(DP1_PLLCTRL, PLLUPDATE | PLLEN); + tc_wait_pll_lock(tc); + + ret = tc_poll_timeout(tc->regmap, DP_PHY_CTRL, PHY_RDY, PHY_RDY, 1, + 1000); + if (ret == -ETIMEDOUT) { + dev_err(tc->dev, "Timeout waiting for PHY to become ready"); + return ret; + } else if (ret) + goto err; + + /* Setup AUX link */ + tc_write(DP0_AUXCFG1, AUX_RX_FILTER_EN | + (0x06 << 8) | /* Aux Bit Period Calculator Threshold */ + (0x3f << 0)); /* Aux Response Timeout Timer */ + + return 0; +err: + dev_err(tc->dev, "tc_aux_link_setup failed: %d\n", ret); + return ret; +} + +static int tc_get_display_props(struct tc_data *tc) +{ + int ret; + /* temp buffer */ + u8 tmp[8]; + + /* Read DP Rx Link Capability */ + ret = drm_dp_link_probe(&tc->aux, &tc->link.base); + if (ret < 0) + goto err_dpcd_read; + if ((tc->link.base.rate != 162000) && (tc->link.base.rate != 270000)) + goto err_dpcd_inval; + + ret = drm_dp_dpcd_readb(&tc->aux, DP_MAX_DOWNSPREAD, tmp); + if (ret < 0) + goto err_dpcd_read; + tc->link.spread = tmp[0] & BIT(0); /* 0.5% down spread */ + + ret = drm_dp_dpcd_readb(&tc->aux, DP_MAIN_LINK_CHANNEL_CODING, tmp); + if (ret < 0) + goto err_dpcd_read; + tc->link.coding8b10b = tmp[0] & BIT(0); + tc->link.scrambler_dis = 0; + /* read assr */ + ret = drm_dp_dpcd_readb(&tc->aux, DP_EDP_CONFIGURATION_SET, tmp); + if (ret < 0) + goto err_dpcd_read; + tc->link.assr = tmp[0] & DP_ALTERNATE_SCRAMBLER_RESET_ENABLE; + + dev_dbg(tc->dev, "DPCD rev: %d.%d, rate: %s, lanes: %d, framing: %s\n", + tc->link.base.revision >> 4, tc->link.base.revision & 0x0f, + (tc->link.base.rate == 162000) ? "1.62Gbps" : "2.7Gbps", + tc->link.base.num_lanes, + (tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) ? + "enhanced" : "non-enhanced"); + dev_dbg(tc->dev, "ANSI 8B/10B: %d\n", tc->link.coding8b10b); + dev_dbg(tc->dev, "Display ASSR: %d, TC358767 ASSR: %d\n", + tc->link.assr, tc->assr); + + return 0; + +err_dpcd_read: + dev_err(tc->dev, "failed to read DPCD: %d\n", ret); + return ret; +err_dpcd_inval: + dev_err(tc->dev, "invalid DPCD\n"); + return -EINVAL; +} + +static int tc_set_video_mode(struct tc_data *tc, struct drm_display_mode *mode) +{ + int ret; + int vid_sync_dly; + int max_tu_symbol; + + int left_margin = mode->htotal - mode->hsync_end; + int right_margin = mode->hsync_start - mode->hdisplay; + int hsync_len = mode->hsync_end - mode->hsync_start; + int upper_margin = mode->vtotal - mode->vsync_end; + int lower_margin = mode->vsync_start - mode->vdisplay; + int vsync_len = mode->vsync_end - mode->vsync_start; + + dev_dbg(tc->dev, "set mode %dx%d\n", + mode->hdisplay, mode->vdisplay); + dev_dbg(tc->dev, "H margin %d,%d sync %d\n", + left_margin, right_margin, hsync_len); + dev_dbg(tc->dev, "V margin %d,%d sync %d\n", + upper_margin, lower_margin, vsync_len); + dev_dbg(tc->dev, "total: %dx%d\n", mode->htotal, mode->vtotal); + + + /* LCD Ctl Frame Size */ + tc_write(VPCTRL0, (0x40 << 20) /* VSDELAY */ | + OPXLFMT_RGB888 | FRMSYNC_DISABLED | MSF_DISABLED); + tc_write(HTIM01, (left_margin << 16) | /* H back porch */ + (hsync_len << 0)); /* Hsync */ + tc_write(HTIM02, (right_margin << 16) | /* H front porch */ + (mode->hdisplay << 0)); /* width */ + tc_write(VTIM01, (upper_margin << 16) | /* V back porch */ + (vsync_len << 0)); /* Vsync */ + tc_write(VTIM02, (lower_margin << 16) | /* V front porch */ + (mode->vdisplay << 0)); /* height */ + tc_write(VFUEN0, VFUEN); /* update settings */ + + /* Test pattern settings */ + tc_write(TSTCTL, + (120 << 24) | /* Red Color component value */ + (20 << 16) | /* Green Color component value */ + (99 << 8) | /* Blue Color component value */ + (1 << 4) | /* Enable I2C Filter */ + (2 << 0) | /* Color bar Mode */ + 0); + + /* DP Main Stream Attributes */ + vid_sync_dly = hsync_len + left_margin + mode->hdisplay; + tc_write(DP0_VIDSYNCDELAY, + (0x003e << 16) | /* thresh_dly */ + (vid_sync_dly << 0)); + + tc_write(DP0_TOTALVAL, (mode->vtotal << 16) | (mode->htotal)); + + tc_write(DP0_STARTVAL, + ((upper_margin + vsync_len) << 16) | + ((left_margin + hsync_len) << 0)); + + tc_write(DP0_ACTIVEVAL, (mode->vdisplay << 16) | (mode->hdisplay)); + + tc_write(DP0_SYNCVAL, (vsync_len << 16) | (hsync_len << 0)); + + tc_write(DPIPXLFMT, VS_POL_ACTIVE_LOW | HS_POL_ACTIVE_LOW | + DE_POL_ACTIVE_HIGH | SUB_CFG_TYPE_CONFIG1 | DPI_BPP_RGB888); + + /* + * Recommended maximum number of symbols transferred in a transfer unit: + * DIV_ROUND_UP((input active video bandwidth in bytes) * tu_size, + * (output active video bandwidth in bytes)) + * Must be less than tu_size. + */ + max_tu_symbol = TU_SIZE_RECOMMENDED - 1; + tc_write(DP0_MISC, (max_tu_symbol << 23) | TU_SIZE_RECOMMENDED | BPC_8); + + return 0; +err: + return ret; +} + +static int tc_link_training(struct tc_data *tc, int pattern) +{ + const char * const *errors; + u32 srcctrl = tc_srcctrl(tc) | DP0_SRCCTRL_SCRMBLDIS | + DP0_SRCCTRL_AUTOCORRECT; + int timeout; + int retry; + u32 value; + int ret; + + if (pattern == DP_TRAINING_PATTERN_1) { + srcctrl |= DP0_SRCCTRL_TP1; + errors = training_pattern1_errors; + } else { + srcctrl |= DP0_SRCCTRL_TP2; + errors = training_pattern2_errors; + } + + /* Set DPCD 0x102 for Training Part 1 or 2 */ + tc_write(DP0_SNKLTCTRL, DP_LINK_SCRAMBLING_DISABLE | pattern); + + tc_write(DP0_LTLOOPCTRL, + (0x0f << 28) | /* Defer Iteration Count */ + (0x0f << 24) | /* Loop Iteration Count */ + (0x0d << 0)); /* Loop Timer Delay */ + + retry = 5; + do { + /* Set DP0 Training Pattern */ + tc_write(DP0_SRCCTRL, srcctrl); + + /* Enable DP0 to start Link Training */ + tc_write(DP0CTL, DP_EN); + + /* wait */ + timeout = 1000; + do { + tc_read(DP0_LTSTAT, &value); + udelay(1); + } while ((!(value & LT_LOOPDONE)) && (--timeout)); + if (timeout == 0) { + dev_err(tc->dev, "Link training timeout!\n"); + } else { + int pattern = (value >> 11) & 0x3; + int error = (value >> 8) & 0x7; + + dev_dbg(tc->dev, + "Link training phase %d done after %d uS: %s\n", + pattern, 1000 - timeout, errors[error]); + if (pattern == DP_TRAINING_PATTERN_1 && error == 0) + break; + if (pattern == DP_TRAINING_PATTERN_2) { + value &= LT_CHANNEL1_EQ_BITS | + LT_INTERLANE_ALIGN_DONE | + LT_CHANNEL0_EQ_BITS; + /* in case of two lanes */ + if ((tc->link.base.num_lanes == 2) && + (value == (LT_CHANNEL1_EQ_BITS | + LT_INTERLANE_ALIGN_DONE | + LT_CHANNEL0_EQ_BITS))) + break; + /* in case of one line */ + if ((tc->link.base.num_lanes == 1) && + (value == (LT_INTERLANE_ALIGN_DONE | + LT_CHANNEL0_EQ_BITS))) + break; + } + } + /* restart */ + tc_write(DP0CTL, 0); + usleep_range(10, 20); + } while (--retry); + if (retry == 0) { + dev_err(tc->dev, "Failed to finish training phase %d\n", + pattern); + } + + return 0; +err: + return ret; +} + +static int tc_main_link_setup(struct tc_data *tc) +{ + struct drm_dp_aux *aux = &tc->aux; + struct device *dev = tc->dev; + unsigned int rate; + u32 dp_phy_ctrl; + int timeout; + bool aligned; + bool ready; + u32 value; + int ret; + u8 tmp[8]; + + /* display mode should be set at this point */ + if (!tc->mode) + return -EINVAL; + + /* from excel file - DP0_SrcCtrl */ + tc_write(DP0_SRCCTRL, DP0_SRCCTRL_SCRMBLDIS | DP0_SRCCTRL_EN810B | + DP0_SRCCTRL_LANESKEW | DP0_SRCCTRL_LANES_2 | + DP0_SRCCTRL_BW27 | DP0_SRCCTRL_AUTOCORRECT); + /* from excel file - DP1_SrcCtrl */ + tc_write(0x07a0, 0x00003083); + + rate = clk_get_rate(tc->refclk); + switch (rate) { + case 38400000: + value = REF_FREQ_38M4; + break; + case 26000000: + value = REF_FREQ_26M; + break; + case 19200000: + value = REF_FREQ_19M2; + break; + case 13000000: + value = REF_FREQ_13M; + break; + default: + return -EINVAL; + } + value |= SYSCLK_SEL_LSCLK | LSCLK_DIV_2; + tc_write(SYS_PLLPARAM, value); + /* Setup Main Link */ + dp_phy_ctrl = BGREN | PWR_SW_EN | BIT(2) | PHY_A0_EN | PHY_M0_EN; + tc_write(DP_PHY_CTRL, dp_phy_ctrl); + msleep(100); + + /* PLL setup */ + tc_write(DP0_PLLCTRL, PLLUPDATE | PLLEN); + tc_wait_pll_lock(tc); + + tc_write(DP1_PLLCTRL, PLLUPDATE | PLLEN); + tc_wait_pll_lock(tc); + + /* PXL PLL setup */ + if (tc_test_pattern) { + ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk), + 1000 * tc->mode->clock); + if (ret) + goto err; + } + + /* Reset/Enable Main Links */ + dp_phy_ctrl |= DP_PHY_RST | PHY_M1_RST | PHY_M0_RST; + tc_write(DP_PHY_CTRL, dp_phy_ctrl); + usleep_range(100, 200); + dp_phy_ctrl &= ~(DP_PHY_RST | PHY_M1_RST | PHY_M0_RST); + tc_write(DP_PHY_CTRL, dp_phy_ctrl); + + timeout = 1000; + do { + tc_read(DP_PHY_CTRL, &value); + udelay(1); + } while ((!(value & PHY_RDY)) && (--timeout)); + + if (timeout == 0) { + dev_err(dev, "timeout waiting for phy become ready"); + return -ETIMEDOUT; + } + + /* Set misc: 8 bits per color */ + ret = regmap_update_bits(tc->regmap, DP0_MISC, BPC_8, BPC_8); + if (ret) + goto err; + + /* + * ASSR mode + * on TC358767 side ASSR configured through strap pin + * seems there is no way to change this setting from SW + * + * check is tc configured for same mode + */ + if (tc->assr != tc->link.assr) { + dev_dbg(dev, "Trying to set display to ASSR: %d\n", + tc->assr); + /* try to set ASSR on display side */ + tmp[0] = tc->assr; + ret = drm_dp_dpcd_writeb(aux, DP_EDP_CONFIGURATION_SET, tmp[0]); + if (ret < 0) + goto err_dpcd_read; + /* read back */ + ret = drm_dp_dpcd_readb(aux, DP_EDP_CONFIGURATION_SET, tmp); + if (ret < 0) + goto err_dpcd_read; + + if (tmp[0] != tc->assr) { + dev_warn(dev, "Failed to switch display ASSR to %d, falling back to unscrambled mode\n", + tc->assr); + /* trying with disabled scrambler */ + tc->link.scrambler_dis = 1; + } + } + + /* Setup Link & DPRx Config for Training */ + ret = drm_dp_link_configure(aux, &tc->link.base); + if (ret < 0) + goto err_dpcd_write; + + /* DOWNSPREAD_CTRL */ + tmp[0] = tc->link.spread ? DP_SPREAD_AMP_0_5 : 0x00; + /* MAIN_LINK_CHANNEL_CODING_SET */ + tmp[1] = tc->link.coding8b10b ? DP_SET_ANSI_8B10B : 0x00; + ret = drm_dp_dpcd_write(aux, DP_DOWNSPREAD_CTRL, tmp, 2); + if (ret < 0) + goto err_dpcd_write; + + ret = tc_link_training(tc, DP_TRAINING_PATTERN_1); + if (ret) + goto err; + + ret = tc_link_training(tc, DP_TRAINING_PATTERN_2); + if (ret) + goto err; + + /* Clear DPCD 0x102 */ + /* Note: Can Not use DP0_SNKLTCTRL (0x06E4) short cut */ + tmp[0] = tc->link.scrambler_dis ? DP_LINK_SCRAMBLING_DISABLE : 0x00; + ret = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, tmp[0]); + if (ret < 0) + goto err_dpcd_write; + + /* Clear Training Pattern, set AutoCorrect Mode = 1 */ + tc_write(DP0_SRCCTRL, tc_srcctrl(tc) | DP0_SRCCTRL_AUTOCORRECT); + + /* Wait */ + timeout = 100; + do { + udelay(1); + /* Read DPCD 0x202-0x207 */ + ret = drm_dp_dpcd_read_link_status(aux, tmp + 2); + if (ret < 0) + goto err_dpcd_read; + ready = (tmp[2] == ((DP_CHANNEL_EQ_BITS << 4) | /* Lane1 */ + DP_CHANNEL_EQ_BITS)); /* Lane0 */ + aligned = tmp[4] & DP_INTERLANE_ALIGN_DONE; + } while ((--timeout) && !(ready && aligned)); + + if (timeout == 0) { + /* Read DPCD 0x200-0x201 */ + ret = drm_dp_dpcd_read(aux, DP_SINK_COUNT, tmp, 2); + if (ret < 0) + goto err_dpcd_read; + dev_info(dev, "0x0200 SINK_COUNT: 0x%02x\n", tmp[0]); + dev_info(dev, "0x0201 DEVICE_SERVICE_IRQ_VECTOR: 0x%02x\n", + tmp[1]); + dev_info(dev, "0x0202 LANE0_1_STATUS: 0x%02x\n", tmp[2]); + dev_info(dev, "0x0204 LANE_ALIGN_STATUS_UPDATED: 0x%02x\n", + tmp[4]); + dev_info(dev, "0x0205 SINK_STATUS: 0x%02x\n", tmp[5]); + dev_info(dev, "0x0206 ADJUST_REQUEST_LANE0_1: 0x%02x\n", + tmp[6]); + + if (!ready) + dev_err(dev, "Lane0/1 not ready\n"); + if (!aligned) + dev_err(dev, "Lane0/1 not aligned\n"); + return -EAGAIN; + } + + ret = tc_set_video_mode(tc, tc->mode); + if (ret) + goto err; + + /* Set M/N */ + ret = tc_stream_clock_calc(tc); + if (ret) + goto err; + + return 0; +err_dpcd_read: + dev_err(tc->dev, "Failed to read DPCD: %d\n", ret); + return ret; +err_dpcd_write: + dev_err(tc->dev, "Failed to write DPCD: %d\n", ret); +err: + return ret; +} + +static int tc_main_link_stream(struct tc_data *tc, int state) +{ + int ret; + u32 value; + + dev_dbg(tc->dev, "stream: %d\n", state); + + if (state) { + value = VID_MN_GEN | DP_EN; + if (tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) + value |= EF_EN; + tc_write(DP0CTL, value); + /* + * VID_EN assertion should be delayed by at least N * LSCLK + * cycles from the time VID_MN_GEN is enabled in order to + * generate stable values for VID_M. LSCLK is 270 MHz or + * 162 MHz, VID_N is set to 32768 in tc_stream_clock_calc(), + * so a delay of at least 203 us should suffice. + */ + usleep_range(500, 1000); + value |= VID_EN; + tc_write(DP0CTL, value); + /* Set input interface */ + value = DP0_AUDSRC_NO_INPUT; + if (tc_test_pattern) + value |= DP0_VIDSRC_COLOR_BAR; + else + value |= DP0_VIDSRC_DPI_RX; + tc_write(SYSCTRL, value); + } else { + tc_write(DP0CTL, 0); + } + + return 0; +err: + return ret; +} + +static enum drm_connector_status +tc_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static void tc_bridge_pre_enable(struct drm_bridge *bridge) +{ + struct tc_data *tc = bridge_to_tc(bridge); + + drm_panel_prepare(tc->panel); +} + +static void tc_bridge_enable(struct drm_bridge *bridge) +{ + struct tc_data *tc = bridge_to_tc(bridge); + int ret; + + ret = tc_main_link_setup(tc); + if (ret < 0) { + dev_err(tc->dev, "main link setup error: %d\n", ret); + return; + } + + ret = tc_main_link_stream(tc, 1); + if (ret < 0) { + dev_err(tc->dev, "main link stream start error: %d\n", ret); + return; + } + + drm_panel_enable(tc->panel); +} + +static void tc_bridge_disable(struct drm_bridge *bridge) +{ + struct tc_data *tc = bridge_to_tc(bridge); + int ret; + + drm_panel_disable(tc->panel); + + ret = tc_main_link_stream(tc, 0); + if (ret < 0) + dev_err(tc->dev, "main link stream stop error: %d\n", ret); +} + +static void tc_bridge_post_disable(struct drm_bridge *bridge) +{ + struct tc_data *tc = bridge_to_tc(bridge); + + drm_panel_unprepare(tc->panel); +} + +static bool tc_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adj) +{ + /* Fixup sync polarities, both hsync and vsync are active low */ + adj->flags = mode->flags; + adj->flags |= (DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC); + adj->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC); + + return true; +} + +static int tc_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + /* Accept any mode */ + return MODE_OK; +} + +static void tc_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adj) +{ + struct tc_data *tc = bridge_to_tc(bridge); + + tc->mode = mode; +} + +static int tc_connector_get_modes(struct drm_connector *connector) +{ + struct tc_data *tc = connector_to_tc(connector); + struct edid *edid; + unsigned int count; + + if (tc->panel && tc->panel->funcs && tc->panel->funcs->get_modes) { + count = tc->panel->funcs->get_modes(tc->panel); + if (count > 0) + return count; + } + + edid = drm_get_edid(connector, &tc->aux.ddc); + + kfree(tc->edid); + tc->edid = edid; + if (!edid) + return 0; + + drm_mode_connector_update_edid_property(connector, edid); + count = drm_add_edid_modes(connector, edid); + + return count; +} + +static void tc_connector_set_polling(struct tc_data *tc, + struct drm_connector *connector) +{ + /* TODO: add support for HPD */ + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; +} + +static struct drm_encoder * +tc_connector_best_encoder(struct drm_connector *connector) +{ + struct tc_data *tc = connector_to_tc(connector); + + return tc->bridge.encoder; +} + +static const struct drm_connector_helper_funcs tc_connector_helper_funcs = { + .get_modes = tc_connector_get_modes, + .mode_valid = tc_connector_mode_valid, + .best_encoder = tc_connector_best_encoder, +}; + +static void tc_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs tc_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = tc_connector_detect, + .destroy = tc_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int tc_bridge_attach(struct drm_bridge *bridge) +{ + u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; + struct tc_data *tc = bridge_to_tc(bridge); + struct drm_device *drm = bridge->dev; + int ret; + + /* Create eDP connector */ + drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs); + ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs, + DRM_MODE_CONNECTOR_eDP); + if (ret) + return ret; + + if (tc->panel) + drm_panel_attach(tc->panel, &tc->connector); + + drm_display_info_set_bus_formats(&tc->connector.display_info, + &bus_format, 1); + drm_mode_connector_attach_encoder(&tc->connector, tc->bridge.encoder); + + return 0; +} + +static const struct drm_bridge_funcs tc_bridge_funcs = { + .attach = tc_bridge_attach, + .mode_set = tc_bridge_mode_set, + .pre_enable = tc_bridge_pre_enable, + .enable = tc_bridge_enable, + .disable = tc_bridge_disable, + .post_disable = tc_bridge_post_disable, + .mode_fixup = tc_bridge_mode_fixup, +}; + +static bool tc_readable_reg(struct device *dev, unsigned int reg) +{ + return reg != SYSCTRL; +} + +static const struct regmap_range tc_volatile_ranges[] = { + regmap_reg_range(DP0_AUXWDATA(0), DP0_AUXSTATUS), + regmap_reg_range(DP0_LTSTAT, DP0_SNKLTCHGREQ), + regmap_reg_range(DP_PHY_CTRL, DP_PHY_CTRL), + regmap_reg_range(DP0_PLLCTRL, PXL_PLLCTRL), + regmap_reg_range(VFUEN0, VFUEN0), +}; + +static const struct regmap_access_table tc_volatile_table = { + .yes_ranges = tc_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(tc_volatile_ranges), +}; + +static bool tc_writeable_reg(struct device *dev, unsigned int reg) +{ + return (reg != TC_IDREG) && + (reg != DP0_LTSTAT) && + (reg != DP0_SNKLTCHGREQ); +} + +static const struct regmap_config tc_regmap_config = { + .name = "tc358767", + .reg_bits = 16, + .val_bits = 32, + .reg_stride = 4, + .max_register = PLL_DBG, + .cache_type = REGCACHE_RBTREE, + .readable_reg = tc_readable_reg, + .volatile_table = &tc_volatile_table, + .writeable_reg = tc_writeable_reg, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *ep; + struct tc_data *tc; + int ret; + + tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL); + if (!tc) + return -ENOMEM; + + tc->dev = dev; + + /* port@2 is the output port */ + ep = of_graph_get_endpoint_by_regs(dev->of_node, 2, -1); + if (ep) { + struct device_node *remote; + + remote = of_graph_get_remote_port_parent(ep); + if (!remote) { + dev_warn(dev, "endpoint %s not connected\n", + ep->full_name); + of_node_put(ep); + return -ENODEV; + } + of_node_put(ep); + tc->panel = of_drm_find_panel(remote); + if (tc->panel) { + dev_dbg(dev, "found panel %s\n", remote->full_name); + } else { + dev_dbg(dev, "waiting for panel %s\n", + remote->full_name); + of_node_put(remote); + return -EPROBE_DEFER; + } + of_node_put(remote); + } + + /* Shut down GPIO is optional */ + tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); + if (IS_ERR(tc->sd_gpio)) + return PTR_ERR(tc->sd_gpio); + + if (tc->sd_gpio) { + gpiod_set_value_cansleep(tc->sd_gpio, 0); + usleep_range(5000, 10000); + } + + /* Reset GPIO is optional */ + tc->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(tc->reset_gpio)) + return PTR_ERR(tc->reset_gpio); + + if (tc->reset_gpio) { + gpiod_set_value_cansleep(tc->reset_gpio, 1); + usleep_range(5000, 10000); + } + + tc->refclk = devm_clk_get(dev, "ref"); + if (IS_ERR(tc->refclk)) { + ret = PTR_ERR(tc->refclk); + dev_err(dev, "Failed to get refclk: %d\n", ret); + return ret; + } + + tc->regmap = devm_regmap_init_i2c(client, &tc_regmap_config); + if (IS_ERR(tc->regmap)) { + ret = PTR_ERR(tc->regmap); + dev_err(dev, "Failed to initialize regmap: %d\n", ret); + return ret; + } + + ret = regmap_read(tc->regmap, TC_IDREG, &tc->rev); + if (ret) { + dev_err(tc->dev, "can not read device ID: %d\n", ret); + return ret; + } + + if ((tc->rev != 0x6601) && (tc->rev != 0x6603)) { + dev_err(tc->dev, "invalid device ID: 0x%08x\n", tc->rev); + return -EINVAL; + } + + tc->assr = (tc->rev == 0x6601); /* Enable ASSR for eDP panels */ + + ret = tc_aux_link_setup(tc); + if (ret) + return ret; + + /* Register DP AUX channel */ + tc->aux.name = "TC358767 AUX i2c adapter"; + tc->aux.dev = tc->dev; + tc->aux.transfer = tc_aux_transfer; + ret = drm_dp_aux_register(&tc->aux); + if (ret) + return ret; + + ret = tc_get_display_props(tc); + if (ret) + goto err_unregister_aux; + + tc_connector_set_polling(tc, &tc->connector); + + tc->bridge.funcs = &tc_bridge_funcs; + tc->bridge.of_node = dev->of_node; + ret = drm_bridge_add(&tc->bridge); + if (ret) { + dev_err(dev, "Failed to add drm_bridge: %d\n", ret); + goto err_unregister_aux; + } + + i2c_set_clientdata(client, tc); + + return 0; +err_unregister_aux: + drm_dp_aux_unregister(&tc->aux); + return ret; +} + +static int tc_remove(struct i2c_client *client) +{ + struct tc_data *tc = i2c_get_clientdata(client); + + drm_bridge_remove(&tc->bridge); + drm_dp_aux_unregister(&tc->aux); + + tc_pxl_pll_dis(tc); + + return 0; +} + +static const struct i2c_device_id tc358767_i2c_ids[] = { + { "tc358767", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tc358767_i2c_ids); + +static const struct of_device_id tc358767_of_ids[] = { + { .compatible = "toshiba,tc358767", }, + { } +}; +MODULE_DEVICE_TABLE(of, tc358767_of_ids); + +static struct i2c_driver tc358767_driver = { + .driver = { + .name = "tc358767", + .of_match_table = tc358767_of_ids, + }, + .id_table = tc358767_i2c_ids, + .probe = tc_probe, + .remove = tc_remove, +}; +module_i2c_driver(tc358767_driver); + +MODULE_AUTHOR("Andrey Gusakov <andrey.gusakov@cogentembedded.com>"); +MODULE_DESCRIPTION("tc358767 eDP encoder driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c index a6e4cd591960..d9d6cc1c8e39 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c @@ -43,7 +43,7 @@ int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev) if (ret) goto err; - ret = fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder); + ret = fsl_dcu_create_outputs(fsl_dev); if (ret) goto err; diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h index 7093109fbc21..5a7b88e19e44 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h @@ -25,9 +25,8 @@ to_fsl_dcu_connector(struct drm_connector *con) : NULL; } -int fsl_dcu_drm_connector_create(struct fsl_dcu_drm_device *fsl_dev, - struct drm_encoder *encoder); int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev, struct drm_crtc *crtc); +int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev); #endif /* __FSL_DCU_DRM_CONNECTOR_H__ */ diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c index 0b0989e503ea..26edcc899712 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -10,6 +10,7 @@ */ #include <linux/backlight.h> +#include <linux/of_graph.h> #include <drm/drmP.h> #include <drm/drm_atomic_helper.h> @@ -132,12 +133,12 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { .mode_valid = fsl_dcu_drm_connector_mode_valid, }; -int fsl_dcu_drm_connector_create(struct fsl_dcu_drm_device *fsl_dev, - struct drm_encoder *encoder) +static int fsl_dcu_attach_panel(struct fsl_dcu_drm_device *fsl_dev, + struct drm_panel *panel) { + struct drm_encoder *encoder = &fsl_dev->encoder; struct drm_connector *connector = &fsl_dev->connector.base; struct drm_mode_config *mode_config = &fsl_dev->drm->mode_config; - struct device_node *panel_node; int ret; fsl_dev->connector.encoder = encoder; @@ -161,21 +162,7 @@ int fsl_dcu_drm_connector_create(struct fsl_dcu_drm_device *fsl_dev, mode_config->dpms_property, DRM_MODE_DPMS_OFF); - panel_node = of_parse_phandle(fsl_dev->np, "fsl,panel", 0); - if (!panel_node) { - dev_err(fsl_dev->dev, "fsl,panel property not found\n"); - ret = -ENODEV; - goto err_sysfs; - } - - fsl_dev->connector.panel = of_drm_find_panel(panel_node); - if (!fsl_dev->connector.panel) { - ret = -EPROBE_DEFER; - goto err_panel; - } - of_node_put(panel_node); - - ret = drm_panel_attach(fsl_dev->connector.panel, connector); + ret = drm_panel_attach(panel, connector); if (ret) { dev_err(fsl_dev->dev, "failed to attach panel\n"); goto err_sysfs; @@ -183,11 +170,62 @@ int fsl_dcu_drm_connector_create(struct fsl_dcu_drm_device *fsl_dev, return 0; -err_panel: - of_node_put(panel_node); err_sysfs: drm_connector_unregister(connector); err_cleanup: drm_connector_cleanup(connector); return ret; } + +static int fsl_dcu_attach_endpoint(struct fsl_dcu_drm_device *fsl_dev, + const struct of_endpoint *ep) +{ + struct drm_bridge *bridge; + struct device_node *np; + + np = of_graph_get_remote_port_parent(ep->local_node); + + fsl_dev->connector.panel = of_drm_find_panel(np); + if (fsl_dev->connector.panel) { + of_node_put(np); + return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel); + } + + bridge = of_drm_find_bridge(np); + of_node_put(np); + if (!bridge) + return -ENODEV; + + fsl_dev->encoder.bridge = bridge; + bridge->encoder = &fsl_dev->encoder; + + return drm_bridge_attach(fsl_dev->drm, bridge); +} + +int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) +{ + struct of_endpoint ep; + struct device_node *ep_node, *panel_node; + int ret; + + /* This is for backward compatibility */ + panel_node = of_parse_phandle(fsl_dev->np, "fsl,panel", 0); + if (panel_node) { + fsl_dev->connector.panel = of_drm_find_panel(panel_node); + of_node_put(panel_node); + if (!fsl_dev->connector.panel) + return -EPROBE_DEFER; + return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel); + } + + ep_node = of_graph_get_next_endpoint(fsl_dev->np, NULL); + if (!ep_node) + return -ENODEV; + + ret = of_graph_parse_endpoint(ep_node, &ep); + of_node_put(ep_node); + if (ret) + return -ENODEV; + + return fsl_dcu_attach_endpoint(fsl_dev, &ep); +} diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c index bbe34f1c0505..bca09ea24632 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c @@ -92,6 +92,7 @@ struct fsl_tcon *fsl_tcon_init(struct device *dev) goto err_node_put; } + of_node_put(np); clk_prepare_enable(tcon->ipg_clk); dev_info(dev, "Using TCON in bypass mode\n"); diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index 22c7ed63a001..4d341db462a2 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig @@ -1,12 +1,6 @@ menu "I2C encoder or helper chips" depends on DRM && DRM_KMS_HELPER && I2C -config DRM_I2C_ADV7511 - tristate "AV7511 encoder" - select REGMAP_I2C - help - Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders. - config DRM_I2C_CH7006 tristate "Chrontel ch7006 TV encoder" default m if DRM_NOUVEAU diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile index 2c72eb584ab7..43aa33baebed 100644 --- a/drivers/gpu/drm/i2c/Makefile +++ b/drivers/gpu/drm/i2c/Makefile @@ -1,7 +1,5 @@ ccflags-y := -Iinclude/drm -obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o - ch7006-y := ch7006_drv.o ch7006_mode.o obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o diff --git a/drivers/gpu/drm/i915/i915_gem_shrinker.c b/drivers/gpu/drm/i915/i915_gem_shrinker.c index 067632ad2f29..6f10b421487b 100644 --- a/drivers/gpu/drm/i915/i915_gem_shrinker.c +++ b/drivers/gpu/drm/i915/i915_gem_shrinker.c @@ -40,7 +40,7 @@ static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task) if (!mutex_is_locked(mutex)) return false; -#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_MUTEXES) +#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER) return mutex->owner == task; #else /* Since UP may be pre-empted, we cannot assume that we own the lock */ diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c index c3b33a10c15c..3edb9580928e 100644 --- a/drivers/gpu/drm/i915/intel_csr.c +++ b/drivers/gpu/drm/i915/intel_csr.c @@ -41,15 +41,15 @@ * be moved to FW_FAILED. */ -#define I915_CSR_KBL "i915/kbl_dmc_ver1_01.bin" +#define I915_CSR_KBL "i915/kbl_dmc_ver1.bin" MODULE_FIRMWARE(I915_CSR_KBL); #define KBL_CSR_VERSION_REQUIRED CSR_VERSION(1, 1) -#define I915_CSR_SKL "i915/skl_dmc_ver1_26.bin" +#define I915_CSR_SKL "i915/skl_dmc_ver1.bin" MODULE_FIRMWARE(I915_CSR_SKL); -#define SKL_CSR_VERSION_REQUIRED CSR_VERSION(1, 26) +#define SKL_CSR_VERSION_REQUIRED CSR_VERSION(1, 23) -#define I915_CSR_BXT "i915/bxt_dmc_ver1_07.bin" +#define I915_CSR_BXT "i915/bxt_dmc_ver1.bin" MODULE_FIRMWARE(I915_CSR_BXT); #define BXT_CSR_VERSION_REQUIRED CSR_VERSION(1, 7) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index be3b2cab2640..70555c526f6c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -12016,6 +12016,12 @@ static int intel_crtc_atomic_check(struct drm_crtc *crtc, ret = intel_color_check(crtc, crtc_state); if (ret) return ret; + + /* + * Changing color management on Intel hardware is + * handled as part of planes update. + */ + crtc_state->planes_changed = true; } ret = 0; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 0c5ba3410a1e..21b04c3eda41 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4336,7 +4336,7 @@ intel_dp_detect(struct drm_connector *connector, bool force) intel_dp->detect_done = false; - if (intel_connector->detect_edid) + if (is_edp(intel_dp) || intel_connector->detect_edid) return connector_status_connected; else return connector_status_disconnected; diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 70c699043d0e..414ddda43922 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1097,6 +1097,7 @@ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine, uint32_t *const batch, uint32_t index) { + struct drm_i915_private *dev_priv = engine->i915; uint32_t l3sqc4_flush = (0x40400000 | GEN8_LQSC_FLUSH_COHERENT_LINES); /* @@ -1105,8 +1106,8 @@ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine, * this batch updates GEN8_L3SQCREG4 with default value we need to * set this bit here to retain the WA during flush. */ - if (IS_SKL_REVID(engine->i915, 0, SKL_REVID_E0) || - IS_KBL_REVID(engine->i915, 0, KBL_REVID_E0)) + if (IS_SKL_REVID(dev_priv, 0, SKL_REVID_E0) || + IS_KBL_REVID(dev_priv, 0, KBL_REVID_E0)) l3sqc4_flush |= GEN8_LQSC_RO_PERF_DIS; wa_ctx_emit(batch, index, (MI_STORE_REGISTER_MEM_GEN8 | @@ -1267,11 +1268,12 @@ static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine, uint32_t *offset) { int ret; + struct drm_i915_private *dev_priv = engine->i915; uint32_t index = wa_ctx_start(wa_ctx, *offset, CACHELINE_DWORDS); /* WaDisableCtxRestoreArbitration:skl,bxt */ - if (IS_SKL_REVID(engine->i915, 0, SKL_REVID_D0) || - IS_BXT_REVID(engine->i915, 0, BXT_REVID_A1)) + if (IS_SKL_REVID(dev_priv, 0, SKL_REVID_D0) || + IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1)) wa_ctx_emit(batch, index, MI_ARB_ON_OFF | MI_ARB_DISABLE); /* WaFlushCoherentL3CacheLinesAtContextSwitch:skl,bxt */ @@ -1282,7 +1284,7 @@ static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine, /* WaClearSlmSpaceAtContextSwitch:kbl */ /* Actual scratch location is at 128 bytes offset */ - if (IS_KBL_REVID(engine->i915, 0, KBL_REVID_A0)) { + if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_A0)) { uint32_t scratch_addr = engine->scratch.gtt_offset + 2*CACHELINE_BYTES; diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index c27d5eb063d0..adca262d591a 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -1072,5 +1072,16 @@ intel_opregion_get_panel_type(struct drm_i915_private *dev_priv) return -ENODEV; } + /* + * FIXME On Dell XPS 13 9350 the OpRegion panel type (0) gives us + * low vswing for eDP, whereas the VBT panel type (2) gives us normal + * vswing instead. Low vswing results in some display flickers, so + * let's simply ignore the OpRegion panel type on SKL for now. + */ + if (IS_SKYLAKE(dev_priv)) { + DRM_DEBUG_KMS("Ignoring OpRegion panel type (%d)\n", ret - 1); + return -ENODEV; + } + return ret - 1; } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 5a8ee0c76593..f4f3fcc8b3be 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -57,7 +57,7 @@ static void gen9_init_clock_gating(struct drm_device *dev) { - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = dev->dev_private; /* See Bspec note for PSR2_CTL bit 31, Wa#828:skl,bxt,kbl */ I915_WRITE(CHICKEN_PAR1_1, @@ -7046,7 +7046,7 @@ static void gen8_set_l3sqc_credits(struct drm_i915_private *dev_priv, static void kabylake_init_clock_gating(struct drm_device *dev) { - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = dev->dev_private; gen9_init_clock_gating(dev); @@ -7067,7 +7067,7 @@ static void kabylake_init_clock_gating(struct drm_device *dev) static void skylake_init_clock_gating(struct drm_device *dev) { - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *dev_priv = dev->dev_private; gen9_init_clock_gating(dev); diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 167a4971f47c..7c7a0314a756 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -10,6 +10,7 @@ config DRM_MSM select SHMEM select TMPFS select QCOM_SCM + select SND_SOC_HDMI_CODEC if SND_SOC default y help DRM/KMS driver for MSM/snapdragon. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 60cb02624dc0..4e2806cf778c 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -35,6 +35,7 @@ msm-y := \ mdp/mdp5/mdp5_crtc.o \ mdp/mdp5/mdp5_encoder.o \ mdp/mdp5/mdp5_irq.o \ + mdp/mdp5/mdp5_mdss.o \ mdp/mdp5/mdp5_kms.o \ mdp/mdp5/mdp5_plane.o \ mdp/mdp5/mdp5_smp.o \ @@ -45,6 +46,7 @@ msm-y := \ msm_fence.o \ msm_gem.o \ msm_gem_prime.o \ + msm_gem_shrinker.o \ msm_gem_submit.o \ msm_gpu.o \ msm_iommu.o \ diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 2aec27dbb5bb..f386f463278d 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -139,7 +139,7 @@ void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct msm_drm_private *priv = gpu->dev->dev_private; struct msm_ringbuffer *ring = gpu->rb; - unsigned i, ibs = 0; + unsigned i; for (i = 0; i < submit->nr_cmds; i++) { switch (submit->cmd[i].type) { @@ -155,18 +155,11 @@ void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, CP_INDIRECT_BUFFER_PFE : CP_INDIRECT_BUFFER_PFD, 2); OUT_RING(ring, submit->cmd[i].iova); OUT_RING(ring, submit->cmd[i].size); - ibs++; + OUT_PKT2(ring); break; } } - /* on a320, at least, we seem to need to pad things out to an - * even number of qwords to avoid issue w/ CP hanging on wrap- - * around: - */ - if (ibs % 2) - OUT_PKT2(ring); - OUT_PKT0(ring, REG_AXXX_CP_SCRATCH_REG2, 1); OUT_RING(ring, submit->fence->seqno); @@ -407,7 +400,7 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, return ret; } - adreno_gpu->memptrs = msm_gem_vaddr(adreno_gpu->memptrs_bo); + adreno_gpu->memptrs = msm_gem_get_vaddr(adreno_gpu->memptrs_bo); if (IS_ERR(adreno_gpu->memptrs)) { dev_err(drm->dev, "could not vmap memptrs\n"); return -ENOMEM; @@ -426,8 +419,12 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, void adreno_gpu_cleanup(struct adreno_gpu *gpu) { if (gpu->memptrs_bo) { + if (gpu->memptrs) + msm_gem_put_vaddr(gpu->memptrs_bo); + if (gpu->memptrs_iova) msm_gem_put_iova(gpu->memptrs_bo, gpu->base.id); + drm_gem_object_unreference_unlocked(gpu->memptrs_bo); } release_firmware(gpu->pm4); diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c index 6edcd6f57e70..ec572f8389ed 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.c +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -29,7 +29,7 @@ static int dsi_get_phy(struct msm_dsi *msm_dsi) struct platform_device *phy_pdev; struct device_node *phy_node; - phy_node = of_parse_phandle(pdev->dev.of_node, "qcom,dsi-phy", 0); + phy_node = of_parse_phandle(pdev->dev.of_node, "phys", 0); if (!phy_node) { dev_err(&pdev->dev, "cannot find phy device\n"); return -ENXIO; diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/drivers/gpu/drm/msm/dsi/dsi_cfg.c index 93c1ee094eac..63436d8ee470 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.c +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.c @@ -29,6 +29,8 @@ static const struct msm_dsi_config apq8064_dsi_cfg = { }, .bus_clk_names = dsi_v2_bus_clk_names, .num_bus_clks = ARRAY_SIZE(dsi_v2_bus_clk_names), + .io_start = { 0x4700000, 0x5800000 }, + .num_dsi = 2, }; static const char * const dsi_6g_bus_clk_names[] = { @@ -48,6 +50,8 @@ static const struct msm_dsi_config msm8974_apq8084_dsi_cfg = { }, .bus_clk_names = dsi_6g_bus_clk_names, .num_bus_clks = ARRAY_SIZE(dsi_6g_bus_clk_names), + .io_start = { 0xfd922800, 0xfd922b00 }, + .num_dsi = 2, }; static const char * const dsi_8916_bus_clk_names[] = { @@ -66,6 +70,8 @@ static const struct msm_dsi_config msm8916_dsi_cfg = { }, .bus_clk_names = dsi_8916_bus_clk_names, .num_bus_clks = ARRAY_SIZE(dsi_8916_bus_clk_names), + .io_start = { 0x1a98000 }, + .num_dsi = 1, }; static const struct msm_dsi_config msm8994_dsi_cfg = { @@ -84,6 +90,8 @@ static const struct msm_dsi_config msm8994_dsi_cfg = { }, .bus_clk_names = dsi_6g_bus_clk_names, .num_bus_clks = ARRAY_SIZE(dsi_6g_bus_clk_names), + .io_start = { 0xfd998000, 0xfd9a0000 }, + .num_dsi = 2, }; static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = { diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.h b/drivers/gpu/drm/msm/dsi/dsi_cfg.h index a68c836744a3..eeacc3232494 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.h +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.h @@ -34,6 +34,8 @@ struct msm_dsi_config { struct dsi_reg_config reg_cfg; const char * const *bus_clk_names; const int num_bus_clks; + const resource_size_t io_start[DSI_MAX]; + const int num_dsi; }; struct msm_dsi_cfg_handler { diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index a3e47ad83eb3..f05ed0e1f3d6 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -1066,7 +1066,7 @@ static int dsi_cmd_dma_add(struct msm_dsi_host *msm_host, } if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) { - data = msm_gem_vaddr(msm_host->tx_gem_obj); + data = msm_gem_get_vaddr(msm_host->tx_gem_obj); if (IS_ERR(data)) { ret = PTR_ERR(data); pr_err("%s: get vaddr failed, %d\n", __func__, ret); @@ -1094,6 +1094,9 @@ static int dsi_cmd_dma_add(struct msm_dsi_host *msm_host, if (packet.size < len) memset(data + packet.size, 0xff, len - packet.size); + if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) + msm_gem_put_vaddr(msm_host->tx_gem_obj); + return len; } @@ -1543,7 +1546,7 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, u32 lane_map[4]; int ret, i, len, num_lanes; - prop = of_find_property(ep, "qcom,data-lane-map", &len); + prop = of_find_property(ep, "data-lanes", &len); if (!prop) { dev_dbg(dev, "failed to find data lane mapping\n"); return -EINVAL; @@ -1558,7 +1561,7 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, msm_host->num_data_lanes = num_lanes; - ret = of_property_read_u32_array(ep, "qcom,data-lane-map", lane_map, + ret = of_property_read_u32_array(ep, "data-lanes", lane_map, num_lanes); if (ret) { dev_err(dev, "failed to read lane data\n"); @@ -1573,8 +1576,19 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, const int *swap = supported_data_lane_swaps[i]; int j; + /* + * the data-lanes array we get from DT has a logical->physical + * mapping. The "data lane swap" register field represents + * supported configurations in a physical->logical mapping. + * Translate the DT mapping to what we understand and find a + * configuration that works. + */ for (j = 0; j < num_lanes; j++) { - if (swap[j] != lane_map[j]) + if (lane_map[j] < 0 || lane_map[j] > 3) + dev_err(dev, "bad physical lane entry %u\n", + lane_map[j]); + + if (swap[lane_map[j]] != j) break; } @@ -1594,20 +1608,13 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) struct device_node *endpoint, *device_node; int ret; - ret = of_property_read_u32(np, "qcom,dsi-host-index", &msm_host->id); - if (ret) { - dev_err(dev, "%s: host index not specified, ret=%d\n", - __func__, ret); - return ret; - } - /* - * Get the first endpoint node. In our case, dsi has one output port - * to which the panel is connected. Don't return an error if a port - * isn't defined. It's possible that there is nothing connected to - * the dsi output. + * Get the endpoint of the output port of the DSI host. In our case, + * this is mapped to port number with reg = 1. Don't return an error if + * the remote endpoint isn't defined. It's possible that there is + * nothing connected to the dsi output. */ - endpoint = of_graph_get_next_endpoint(np, NULL); + endpoint = of_graph_get_endpoint_by_regs(np, 1, -1); if (!endpoint) { dev_dbg(dev, "%s: no endpoint\n", __func__); return 0; @@ -1648,6 +1655,25 @@ err: return ret; } +static int dsi_host_get_id(struct msm_dsi_host *msm_host) +{ + struct platform_device *pdev = msm_host->pdev; + const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; + struct resource *res; + int i; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dsi_ctrl"); + if (!res) + return -EINVAL; + + for (i = 0; i < cfg->num_dsi; i++) { + if (cfg->io_start[i] == res->start) + return i; + } + + return -EINVAL; +} + int msm_dsi_host_init(struct msm_dsi *msm_dsi) { struct msm_dsi_host *msm_host = NULL; @@ -1684,6 +1710,13 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) goto fail; } + msm_host->id = dsi_host_get_id(msm_host); + if (msm_host->id < 0) { + ret = msm_host->id; + pr_err("%s: unable to identify DSI host index\n", __func__); + goto fail; + } + /* fixup base address by io offset */ msm_host->ctrl_base += msm_host->cfg_hnd->cfg->io_offset; @@ -2245,9 +2278,9 @@ int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, } msm_host->mode = drm_mode_duplicate(msm_host->dev, mode); - if (IS_ERR(msm_host->mode)) { + if (!msm_host->mode) { pr_err("%s: cannot duplicate mode\n", __func__); - return PTR_ERR(msm_host->mode); + return -ENOMEM; } return 0; diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c index e2f42d8ea294..f39386ed75e4 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c @@ -271,6 +271,30 @@ static const struct of_device_id dsi_phy_dt_match[] = { {} }; +/* + * Currently, we only support one SoC for each PHY type. When we have multiple + * SoCs for the same PHY, we can try to make the index searching a bit more + * clever. + */ +static int dsi_phy_get_id(struct msm_dsi_phy *phy) +{ + struct platform_device *pdev = phy->pdev; + const struct msm_dsi_phy_cfg *cfg = phy->cfg; + struct resource *res; + int i; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dsi_phy"); + if (!res) + return -EINVAL; + + for (i = 0; i < cfg->num_dsi_phy; i++) { + if (cfg->io_start[i] == res->start) + return i; + } + + return -EINVAL; +} + static int dsi_phy_driver_probe(struct platform_device *pdev) { struct msm_dsi_phy *phy; @@ -289,10 +313,10 @@ static int dsi_phy_driver_probe(struct platform_device *pdev) phy->cfg = match->data; phy->pdev = pdev; - ret = of_property_read_u32(dev->of_node, - "qcom,dsi-phy-index", &phy->id); - if (ret) { - dev_err(dev, "%s: PHY index not specified, %d\n", + phy->id = dsi_phy_get_id(phy); + if (phy->id < 0) { + ret = phy->id; + dev_err(dev, "%s: couldn't identify PHY index, %d\n", __func__, ret); goto fail; } diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h index 0d54ed00386d..f24a85439b94 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h @@ -38,6 +38,8 @@ struct msm_dsi_phy_cfg { * Fill default H/W values in illegal cells, eg. cell {0, 1}. */ bool src_pll_truthtable[DSI_MAX][DSI_MAX]; + const resource_size_t io_start[DSI_MAX]; + const int num_dsi_phy; }; extern const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs; diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c index f4bc11af849a..c757e2070cac 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c @@ -145,6 +145,8 @@ const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs = { .ops = { .enable = dsi_20nm_phy_enable, .disable = dsi_20nm_phy_disable, - } + }, + .io_start = { 0xfd998300, 0xfd9a0300 }, + .num_dsi_phy = 2, }; diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c index 96d1852af418..63d7fba31380 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c @@ -145,6 +145,8 @@ const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs = { .enable = dsi_28nm_phy_enable, .disable = dsi_28nm_phy_disable, }, + .io_start = { 0xfd922b00, 0xfd923100 }, + .num_dsi_phy = 2, }; const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs = { @@ -160,5 +162,7 @@ const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs = { .enable = dsi_28nm_phy_enable, .disable = dsi_28nm_phy_disable, }, + .io_start = { 0x1a98500 }, + .num_dsi_phy = 1, }; diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c index 213355a3e767..7bdb9de54968 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c @@ -192,4 +192,6 @@ const struct msm_dsi_phy_cfg dsi_phy_28nm_8960_cfgs = { .enable = dsi_28nm_phy_enable, .disable = dsi_28nm_phy_disable, }, + .io_start = { 0x4700300, 0x5800300 }, + .num_dsi_phy = 2, }; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 51b9ea552f97..973720792236 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -19,6 +19,7 @@ #include <linux/of_irq.h> #include <linux/of_gpio.h> +#include <sound/hdmi-codec.h> #include "hdmi.h" void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on) @@ -434,6 +435,111 @@ static int msm_hdmi_get_gpio(struct device_node *of_node, const char *name) return gpio; } +/* + * HDMI audio codec callbacks + */ +static int msm_hdmi_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + struct hdmi *hdmi = dev_get_drvdata(dev); + unsigned int chan; + unsigned int channel_allocation = 0; + unsigned int rate; + unsigned int level_shift = 0; /* 0dB */ + bool down_mix = false; + + dev_dbg(dev, "%u Hz, %d bit, %d channels\n", params->sample_rate, + params->sample_width, params->cea.channels); + + switch (params->cea.channels) { + case 2: + /* FR and FL speakers */ + channel_allocation = 0; + chan = MSM_HDMI_AUDIO_CHANNEL_2; + break; + case 4: + /* FC, LFE, FR and FL speakers */ + channel_allocation = 0x3; + chan = MSM_HDMI_AUDIO_CHANNEL_4; + break; + case 6: + /* RR, RL, FC, LFE, FR and FL speakers */ + channel_allocation = 0x0B; + chan = MSM_HDMI_AUDIO_CHANNEL_6; + break; + case 8: + /* FRC, FLC, RR, RL, FC, LFE, FR and FL speakers */ + channel_allocation = 0x1F; + chan = MSM_HDMI_AUDIO_CHANNEL_8; + break; + default: + return -EINVAL; + } + + switch (params->sample_rate) { + case 32000: + rate = HDMI_SAMPLE_RATE_32KHZ; + break; + case 44100: + rate = HDMI_SAMPLE_RATE_44_1KHZ; + break; + case 48000: + rate = HDMI_SAMPLE_RATE_48KHZ; + break; + case 88200: + rate = HDMI_SAMPLE_RATE_88_2KHZ; + break; + case 96000: + rate = HDMI_SAMPLE_RATE_96KHZ; + break; + case 176400: + rate = HDMI_SAMPLE_RATE_176_4KHZ; + break; + case 192000: + rate = HDMI_SAMPLE_RATE_192KHZ; + break; + default: + dev_err(dev, "rate[%d] not supported!\n", + params->sample_rate); + return -EINVAL; + } + + msm_hdmi_audio_set_sample_rate(hdmi, rate); + msm_hdmi_audio_info_setup(hdmi, 1, chan, channel_allocation, + level_shift, down_mix); + + return 0; +} + +static void msm_hdmi_audio_shutdown(struct device *dev, void *data) +{ + struct hdmi *hdmi = dev_get_drvdata(dev); + + msm_hdmi_audio_info_setup(hdmi, 0, 0, 0, 0, 0); +} + +static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = { + .hw_params = msm_hdmi_audio_hw_params, + .audio_shutdown = msm_hdmi_audio_shutdown, +}; + +static struct hdmi_codec_pdata codec_data = { + .ops = &msm_hdmi_audio_codec_ops, + .max_i2s_channels = 8, + .i2s = 1, +}; + +static int msm_hdmi_register_audio_driver(struct hdmi *hdmi, struct device *dev) +{ + hdmi->audio_pdev = platform_device_register_data(dev, + HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, + &codec_data, + sizeof(codec_data)); + return PTR_ERR_OR_ZERO(hdmi->audio_pdev); +} + static int msm_hdmi_bind(struct device *dev, struct device *master, void *data) { struct drm_device *drm = dev_get_drvdata(master); @@ -441,7 +547,7 @@ static int msm_hdmi_bind(struct device *dev, struct device *master, void *data) static struct hdmi_platform_config *hdmi_cfg; struct hdmi *hdmi; struct device_node *of_node = dev->of_node; - int i; + int i, err; hdmi_cfg = (struct hdmi_platform_config *) of_device_get_match_data(dev); @@ -468,6 +574,12 @@ static int msm_hdmi_bind(struct device *dev, struct device *master, void *data) return PTR_ERR(hdmi); priv->hdmi = hdmi; + err = msm_hdmi_register_audio_driver(hdmi, dev); + if (err) { + DRM_ERROR("Failed to attach an audio codec %d\n", err); + hdmi->audio_pdev = NULL; + } + return 0; } @@ -477,6 +589,9 @@ static void msm_hdmi_unbind(struct device *dev, struct device *master, struct drm_device *drm = dev_get_drvdata(master); struct msm_drm_private *priv = drm->dev_private; if (priv->hdmi) { + if (priv->hdmi->audio_pdev) + platform_device_unregister(priv->hdmi->audio_pdev); + msm_hdmi_destroy(priv->hdmi); priv->hdmi = NULL; } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index bc7ba0bdee07..accc9a61611d 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -50,6 +50,7 @@ struct hdmi_hdcp_ctrl; struct hdmi { struct drm_device *dev; struct platform_device *pdev; + struct platform_device *audio_pdev; const struct hdmi_platform_config *config; @@ -210,6 +211,19 @@ static inline int msm_hdmi_pll_8996_init(struct platform_device *pdev) /* * audio: */ +/* Supported HDMI Audio channels and rates */ +#define MSM_HDMI_AUDIO_CHANNEL_2 0 +#define MSM_HDMI_AUDIO_CHANNEL_4 1 +#define MSM_HDMI_AUDIO_CHANNEL_6 2 +#define MSM_HDMI_AUDIO_CHANNEL_8 3 + +#define HDMI_SAMPLE_RATE_32KHZ 0 +#define HDMI_SAMPLE_RATE_44_1KHZ 1 +#define HDMI_SAMPLE_RATE_48KHZ 2 +#define HDMI_SAMPLE_RATE_88_2KHZ 3 +#define HDMI_SAMPLE_RATE_96KHZ 4 +#define HDMI_SAMPLE_RATE_176_4KHZ 5 +#define HDMI_SAMPLE_RATE_192KHZ 6 int msm_hdmi_audio_update(struct hdmi *hdmi); int msm_hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c index 0baaaaabd002..6e767979aab3 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c @@ -1430,7 +1430,7 @@ struct hdmi_hdcp_ctrl *msm_hdmi_hdcp_init(struct hdmi *hdmi) void msm_hdmi_hdcp_destroy(struct hdmi *hdmi) { - if (hdmi && hdmi->hdcp_ctrl) { + if (hdmi) { kfree(hdmi->hdcp_ctrl); hdmi->hdcp_ctrl = NULL; } diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c index 35ad78a1dc1c..24258e3025e3 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c @@ -23,7 +23,6 @@ struct mdp4_dtv_encoder { struct drm_encoder base; - struct clk *src_clk; struct clk *hdmi_clk; struct clk *mdp_clk; unsigned long int pixclock; @@ -179,7 +178,6 @@ static void mdp4_dtv_encoder_disable(struct drm_encoder *encoder) */ mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_EXTERNAL_VSYNC); - clk_disable_unprepare(mdp4_dtv_encoder->src_clk); clk_disable_unprepare(mdp4_dtv_encoder->hdmi_clk); clk_disable_unprepare(mdp4_dtv_encoder->mdp_clk); @@ -208,19 +206,21 @@ static void mdp4_dtv_encoder_enable(struct drm_encoder *encoder) bs_set(mdp4_dtv_encoder, 1); - DBG("setting src_clk=%lu", pc); + DBG("setting mdp_clk=%lu", pc); - ret = clk_set_rate(mdp4_dtv_encoder->src_clk, pc); + ret = clk_set_rate(mdp4_dtv_encoder->mdp_clk, pc); if (ret) - dev_err(dev->dev, "failed to set src_clk to %lu: %d\n", pc, ret); - clk_prepare_enable(mdp4_dtv_encoder->src_clk); - ret = clk_prepare_enable(mdp4_dtv_encoder->hdmi_clk); - if (ret) - dev_err(dev->dev, "failed to enable hdmi_clk: %d\n", ret); + dev_err(dev->dev, "failed to set mdp_clk to %lu: %d\n", + pc, ret); + ret = clk_prepare_enable(mdp4_dtv_encoder->mdp_clk); if (ret) dev_err(dev->dev, "failed to enabled mdp_clk: %d\n", ret); + ret = clk_prepare_enable(mdp4_dtv_encoder->hdmi_clk); + if (ret) + dev_err(dev->dev, "failed to enable hdmi_clk: %d\n", ret); + mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 1); mdp4_dtv_encoder->enabled = true; @@ -235,7 +235,7 @@ static const struct drm_encoder_helper_funcs mdp4_dtv_encoder_helper_funcs = { long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate) { struct mdp4_dtv_encoder *mdp4_dtv_encoder = to_mdp4_dtv_encoder(encoder); - return clk_round_rate(mdp4_dtv_encoder->src_clk, rate); + return clk_round_rate(mdp4_dtv_encoder->mdp_clk, rate); } /* initialize encoder */ @@ -257,13 +257,6 @@ struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev) DRM_MODE_ENCODER_TMDS, NULL); drm_encoder_helper_add(encoder, &mdp4_dtv_encoder_helper_funcs); - mdp4_dtv_encoder->src_clk = devm_clk_get(dev->dev, "src_clk"); - if (IS_ERR(mdp4_dtv_encoder->src_clk)) { - dev_err(dev->dev, "failed to get src_clk\n"); - ret = PTR_ERR(mdp4_dtv_encoder->src_clk); - goto fail; - } - mdp4_dtv_encoder->hdmi_clk = devm_clk_get(dev->dev, "hdmi_clk"); if (IS_ERR(mdp4_dtv_encoder->hdmi_clk)) { dev_err(dev->dev, "failed to get hdmi_clk\n"); @@ -271,9 +264,9 @@ struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev) goto fail; } - mdp4_dtv_encoder->mdp_clk = devm_clk_get(dev->dev, "mdp_clk"); + mdp4_dtv_encoder->mdp_clk = devm_clk_get(dev->dev, "tv_clk"); if (IS_ERR(mdp4_dtv_encoder->mdp_clk)) { - dev_err(dev->dev, "failed to get mdp_clk\n"); + dev_err(dev->dev, "failed to get tv_clk\n"); ret = PTR_ERR(mdp4_dtv_encoder->mdp_clk); goto fail; } diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index f145d256e332..7b39e89fbc2b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -158,6 +158,7 @@ static const char * const iommu_ports[] = { static void mdp4_destroy(struct msm_kms *kms) { struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); + struct device *dev = mdp4_kms->dev->dev; struct msm_mmu *mmu = mdp4_kms->mmu; if (mmu) { @@ -167,8 +168,11 @@ static void mdp4_destroy(struct msm_kms *kms) if (mdp4_kms->blank_cursor_iova) msm_gem_put_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id); - if (mdp4_kms->blank_cursor_bo) - drm_gem_object_unreference_unlocked(mdp4_kms->blank_cursor_bo); + drm_gem_object_unreference_unlocked(mdp4_kms->blank_cursor_bo); + + if (mdp4_kms->rpm_enabled) + pm_runtime_disable(dev); + kfree(mdp4_kms); } @@ -436,7 +440,7 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev) struct mdp4_kms *mdp4_kms; struct msm_kms *kms = NULL; struct msm_mmu *mmu; - int ret; + int irq, ret; mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL); if (!mdp4_kms) { @@ -457,6 +461,15 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev) goto fail; } + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + dev_err(dev->dev, "failed to get irq: %d\n", ret); + goto fail; + } + + kms->irq = irq; + /* NOTE: driver for this regulator still missing upstream.. use * _get_exclusive() and ignore the error if it does not exist * (and hope that the bootloader left it on for us) @@ -492,7 +505,7 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev) goto fail; } - mdp4_kms->axi_clk = devm_clk_get(&pdev->dev, "mdp_axi_clk"); + mdp4_kms->axi_clk = devm_clk_get(&pdev->dev, "bus_clk"); if (IS_ERR(mdp4_kms->axi_clk)) { dev_err(dev->dev, "failed to get axi_clk\n"); ret = PTR_ERR(mdp4_kms->axi_clk); @@ -502,6 +515,9 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev) clk_set_rate(mdp4_kms->clk, config->max_clk); clk_set_rate(mdp4_kms->lut_clk, config->max_clk); + pm_runtime_enable(dev->dev); + mdp4_kms->rpm_enabled = true; + /* make sure things are off before attaching iommu (bootloader could * have left things on, in which case we'll start getting faults if * we don't disable): diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h index c5d045d5680d..25fb83997119 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h @@ -47,6 +47,8 @@ struct mdp4_kms { struct mdp_irq error_handler; + bool rpm_enabled; + /* empty/blank cursor bo to use when cursor is "disabled" */ struct drm_gem_object *blank_cursor_bo; uint32_t blank_cursor_iova; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h index b275ce11b24b..ca6ca30650a0 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h @@ -8,19 +8,11 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 27887 bytes, from 2015-10-22 16:34:52) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) - -Copyright (C) 2013-2015 by the following authors: +- /local/mnt/workspace/source_trees/envytools/rnndb/../rnndb/mdp/mdp5.xml ( 36965 bytes, from 2016-05-10 05:06:30) +- /local/mnt/workspace/source_trees/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-05-09 06:32:54) +- /local/mnt/workspace/source_trees/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2016-01-07 08:45:55) + +Copyright (C) 2013-2016 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) - Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) @@ -198,118 +190,109 @@ static inline uint32_t MDSS_HW_VERSION_MAJOR(uint32_t val) #define MDSS_HW_INTR_STATUS_INTR_HDMI 0x00000100 #define MDSS_HW_INTR_STATUS_INTR_EDP 0x00001000 -static inline uint32_t __offset_MDP(uint32_t idx) -{ - switch (idx) { - case 0: return (mdp5_cfg->mdp.base[0]); - default: return INVALID_IDX(idx); - } -} -static inline uint32_t REG_MDP5_MDP(uint32_t i0) { return 0x00000000 + __offset_MDP(i0); } - -static inline uint32_t REG_MDP5_MDP_HW_VERSION(uint32_t i0) { return 0x00000000 + __offset_MDP(i0); } -#define MDP5_MDP_HW_VERSION_STEP__MASK 0x0000ffff -#define MDP5_MDP_HW_VERSION_STEP__SHIFT 0 -static inline uint32_t MDP5_MDP_HW_VERSION_STEP(uint32_t val) +#define REG_MDP5_HW_VERSION 0x00000000 +#define MDP5_HW_VERSION_STEP__MASK 0x0000ffff +#define MDP5_HW_VERSION_STEP__SHIFT 0 +static inline uint32_t MDP5_HW_VERSION_STEP(uint32_t val) { - return ((val) << MDP5_MDP_HW_VERSION_STEP__SHIFT) & MDP5_MDP_HW_VERSION_STEP__MASK; + return ((val) << MDP5_HW_VERSION_STEP__SHIFT) & MDP5_HW_VERSION_STEP__MASK; } -#define MDP5_MDP_HW_VERSION_MINOR__MASK 0x0fff0000 -#define MDP5_MDP_HW_VERSION_MINOR__SHIFT 16 -static inline uint32_t MDP5_MDP_HW_VERSION_MINOR(uint32_t val) +#define MDP5_HW_VERSION_MINOR__MASK 0x0fff0000 +#define MDP5_HW_VERSION_MINOR__SHIFT 16 +static inline uint32_t MDP5_HW_VERSION_MINOR(uint32_t val) { - return ((val) << MDP5_MDP_HW_VERSION_MINOR__SHIFT) & MDP5_MDP_HW_VERSION_MINOR__MASK; + return ((val) << MDP5_HW_VERSION_MINOR__SHIFT) & MDP5_HW_VERSION_MINOR__MASK; } -#define MDP5_MDP_HW_VERSION_MAJOR__MASK 0xf0000000 -#define MDP5_MDP_HW_VERSION_MAJOR__SHIFT 28 -static inline uint32_t MDP5_MDP_HW_VERSION_MAJOR(uint32_t val) +#define MDP5_HW_VERSION_MAJOR__MASK 0xf0000000 +#define MDP5_HW_VERSION_MAJOR__SHIFT 28 +static inline uint32_t MDP5_HW_VERSION_MAJOR(uint32_t val) { - return ((val) << MDP5_MDP_HW_VERSION_MAJOR__SHIFT) & MDP5_MDP_HW_VERSION_MAJOR__MASK; + return ((val) << MDP5_HW_VERSION_MAJOR__SHIFT) & MDP5_HW_VERSION_MAJOR__MASK; } -static inline uint32_t REG_MDP5_MDP_DISP_INTF_SEL(uint32_t i0) { return 0x00000004 + __offset_MDP(i0); } -#define MDP5_MDP_DISP_INTF_SEL_INTF0__MASK 0x000000ff -#define MDP5_MDP_DISP_INTF_SEL_INTF0__SHIFT 0 -static inline uint32_t MDP5_MDP_DISP_INTF_SEL_INTF0(enum mdp5_intf_type val) +#define REG_MDP5_DISP_INTF_SEL 0x00000004 +#define MDP5_DISP_INTF_SEL_INTF0__MASK 0x000000ff +#define MDP5_DISP_INTF_SEL_INTF0__SHIFT 0 +static inline uint32_t MDP5_DISP_INTF_SEL_INTF0(enum mdp5_intf_type val) { - return ((val) << MDP5_MDP_DISP_INTF_SEL_INTF0__SHIFT) & MDP5_MDP_DISP_INTF_SEL_INTF0__MASK; + return ((val) << MDP5_DISP_INTF_SEL_INTF0__SHIFT) & MDP5_DISP_INTF_SEL_INTF0__MASK; } -#define MDP5_MDP_DISP_INTF_SEL_INTF1__MASK 0x0000ff00 -#define MDP5_MDP_DISP_INTF_SEL_INTF1__SHIFT 8 -static inline uint32_t MDP5_MDP_DISP_INTF_SEL_INTF1(enum mdp5_intf_type val) +#define MDP5_DISP_INTF_SEL_INTF1__MASK 0x0000ff00 +#define MDP5_DISP_INTF_SEL_INTF1__SHIFT 8 +static inline uint32_t MDP5_DISP_INTF_SEL_INTF1(enum mdp5_intf_type val) { - return ((val) << MDP5_MDP_DISP_INTF_SEL_INTF1__SHIFT) & MDP5_MDP_DISP_INTF_SEL_INTF1__MASK; + return ((val) << MDP5_DISP_INTF_SEL_INTF1__SHIFT) & MDP5_DISP_INTF_SEL_INTF1__MASK; } -#define MDP5_MDP_DISP_INTF_SEL_INTF2__MASK 0x00ff0000 -#define MDP5_MDP_DISP_INTF_SEL_INTF2__SHIFT 16 -static inline uint32_t MDP5_MDP_DISP_INTF_SEL_INTF2(enum mdp5_intf_type val) +#define MDP5_DISP_INTF_SEL_INTF2__MASK 0x00ff0000 +#define MDP5_DISP_INTF_SEL_INTF2__SHIFT 16 +static inline uint32_t MDP5_DISP_INTF_SEL_INTF2(enum mdp5_intf_type val) { - return ((val) << MDP5_MDP_DISP_INTF_SEL_INTF2__SHIFT) & MDP5_MDP_DISP_INTF_SEL_INTF2__MASK; + return ((val) << MDP5_DISP_INTF_SEL_INTF2__SHIFT) & MDP5_DISP_INTF_SEL_INTF2__MASK; } -#define MDP5_MDP_DISP_INTF_SEL_INTF3__MASK 0xff000000 -#define MDP5_MDP_DISP_INTF_SEL_INTF3__SHIFT 24 -static inline uint32_t MDP5_MDP_DISP_INTF_SEL_INTF3(enum mdp5_intf_type val) +#define MDP5_DISP_INTF_SEL_INTF3__MASK 0xff000000 +#define MDP5_DISP_INTF_SEL_INTF3__SHIFT 24 +static inline uint32_t MDP5_DISP_INTF_SEL_INTF3(enum mdp5_intf_type val) { - return ((val) << MDP5_MDP_DISP_INTF_SEL_INTF3__SHIFT) & MDP5_MDP_DISP_INTF_SEL_INTF3__MASK; + return ((val) << MDP5_DISP_INTF_SEL_INTF3__SHIFT) & MDP5_DISP_INTF_SEL_INTF3__MASK; } -static inline uint32_t REG_MDP5_MDP_INTR_EN(uint32_t i0) { return 0x00000010 + __offset_MDP(i0); } +#define REG_MDP5_INTR_EN 0x00000010 -static inline uint32_t REG_MDP5_MDP_INTR_STATUS(uint32_t i0) { return 0x00000014 + __offset_MDP(i0); } +#define REG_MDP5_INTR_STATUS 0x00000014 -static inline uint32_t REG_MDP5_MDP_INTR_CLEAR(uint32_t i0) { return 0x00000018 + __offset_MDP(i0); } +#define REG_MDP5_INTR_CLEAR 0x00000018 -static inline uint32_t REG_MDP5_MDP_HIST_INTR_EN(uint32_t i0) { return 0x0000001c + __offset_MDP(i0); } +#define REG_MDP5_HIST_INTR_EN 0x0000001c -static inline uint32_t REG_MDP5_MDP_HIST_INTR_STATUS(uint32_t i0) { return 0x00000020 + __offset_MDP(i0); } +#define REG_MDP5_HIST_INTR_STATUS 0x00000020 -static inline uint32_t REG_MDP5_MDP_HIST_INTR_CLEAR(uint32_t i0) { return 0x00000024 + __offset_MDP(i0); } +#define REG_MDP5_HIST_INTR_CLEAR 0x00000024 -static inline uint32_t REG_MDP5_MDP_SPARE_0(uint32_t i0) { return 0x00000028 + __offset_MDP(i0); } -#define MDP5_MDP_SPARE_0_SPLIT_DPL_SINGLE_FLUSH_EN 0x00000001 +#define REG_MDP5_SPARE_0 0x00000028 +#define MDP5_SPARE_0_SPLIT_DPL_SINGLE_FLUSH_EN 0x00000001 -static inline uint32_t REG_MDP5_MDP_SMP_ALLOC_W(uint32_t i0, uint32_t i1) { return 0x00000080 + __offset_MDP(i0) + 0x4*i1; } +static inline uint32_t REG_MDP5_SMP_ALLOC_W(uint32_t i0) { return 0x00000080 + 0x4*i0; } -static inline uint32_t REG_MDP5_MDP_SMP_ALLOC_W_REG(uint32_t i0, uint32_t i1) { return 0x00000080 + __offset_MDP(i0) + 0x4*i1; } -#define MDP5_MDP_SMP_ALLOC_W_REG_CLIENT0__MASK 0x000000ff -#define MDP5_MDP_SMP_ALLOC_W_REG_CLIENT0__SHIFT 0 -static inline uint32_t MDP5_MDP_SMP_ALLOC_W_REG_CLIENT0(uint32_t val) +static inline uint32_t REG_MDP5_SMP_ALLOC_W_REG(uint32_t i0) { return 0x00000080 + 0x4*i0; } +#define MDP5_SMP_ALLOC_W_REG_CLIENT0__MASK 0x000000ff +#define MDP5_SMP_ALLOC_W_REG_CLIENT0__SHIFT 0 +static inline uint32_t MDP5_SMP_ALLOC_W_REG_CLIENT0(uint32_t val) { - return ((val) << MDP5_MDP_SMP_ALLOC_W_REG_CLIENT0__SHIFT) & MDP5_MDP_SMP_ALLOC_W_REG_CLIENT0__MASK; + return ((val) << MDP5_SMP_ALLOC_W_REG_CLIENT0__SHIFT) & MDP5_SMP_ALLOC_W_REG_CLIENT0__MASK; } -#define MDP5_MDP_SMP_ALLOC_W_REG_CLIENT1__MASK 0x0000ff00 -#define MDP5_MDP_SMP_ALLOC_W_REG_CLIENT1__SHIFT 8 -static inline uint32_t MDP5_MDP_SMP_ALLOC_W_REG_CLIENT1(uint32_t val) +#define MDP5_SMP_ALLOC_W_REG_CLIENT1__MASK 0x0000ff00 +#define MDP5_SMP_ALLOC_W_REG_CLIENT1__SHIFT 8 +static inline uint32_t MDP5_SMP_ALLOC_W_REG_CLIENT1(uint32_t val) { - return ((val) << MDP5_MDP_SMP_ALLOC_W_REG_CLIENT1__SHIFT) & MDP5_MDP_SMP_ALLOC_W_REG_CLIENT1__MASK; + return ((val) << MDP5_SMP_ALLOC_W_REG_CLIENT1__SHIFT) & MDP5_SMP_ALLOC_W_REG_CLIENT1__MASK; } -#define MDP5_MDP_SMP_ALLOC_W_REG_CLIENT2__MASK 0x00ff0000 -#define MDP5_MDP_SMP_ALLOC_W_REG_CLIENT2__SHIFT 16 -static inline uint32_t MDP5_MDP_SMP_ALLOC_W_REG_CLIENT2(uint32_t val) +#define MDP5_SMP_ALLOC_W_REG_CLIENT2__MASK 0x00ff0000 +#define MDP5_SMP_ALLOC_W_REG_CLIENT2__SHIFT 16 +static inline uint32_t MDP5_SMP_ALLOC_W_REG_CLIENT2(uint32_t val) { - return ((val) << MDP5_MDP_SMP_ALLOC_W_REG_CLIENT2__SHIFT) & MDP5_MDP_SMP_ALLOC_W_REG_CLIENT2__MASK; + return ((val) << MDP5_SMP_ALLOC_W_REG_CLIENT2__SHIFT) & MDP5_SMP_ALLOC_W_REG_CLIENT2__MASK; } -static inline uint32_t REG_MDP5_MDP_SMP_ALLOC_R(uint32_t i0, uint32_t i1) { return 0x00000130 + __offset_MDP(i0) + 0x4*i1; } +static inline uint32_t REG_MDP5_SMP_ALLOC_R(uint32_t i0) { return 0x00000130 + 0x4*i0; } -static inline uint32_t REG_MDP5_MDP_SMP_ALLOC_R_REG(uint32_t i0, uint32_t i1) { return 0x00000130 + __offset_MDP(i0) + 0x4*i1; } -#define MDP5_MDP_SMP_ALLOC_R_REG_CLIENT0__MASK 0x000000ff -#define MDP5_MDP_SMP_ALLOC_R_REG_CLIENT0__SHIFT 0 -static inline uint32_t MDP5_MDP_SMP_ALLOC_R_REG_CLIENT0(uint32_t val) +static inline uint32_t REG_MDP5_SMP_ALLOC_R_REG(uint32_t i0) { return 0x00000130 + 0x4*i0; } +#define MDP5_SMP_ALLOC_R_REG_CLIENT0__MASK 0x000000ff +#define MDP5_SMP_ALLOC_R_REG_CLIENT0__SHIFT 0 +static inline uint32_t MDP5_SMP_ALLOC_R_REG_CLIENT0(uint32_t val) { - return ((val) << MDP5_MDP_SMP_ALLOC_R_REG_CLIENT0__SHIFT) & MDP5_MDP_SMP_ALLOC_R_REG_CLIENT0__MASK; + return ((val) << MDP5_SMP_ALLOC_R_REG_CLIENT0__SHIFT) & MDP5_SMP_ALLOC_R_REG_CLIENT0__MASK; } -#define MDP5_MDP_SMP_ALLOC_R_REG_CLIENT1__MASK 0x0000ff00 -#define MDP5_MDP_SMP_ALLOC_R_REG_CLIENT1__SHIFT 8 -static inline uint32_t MDP5_MDP_SMP_ALLOC_R_REG_CLIENT1(uint32_t val) +#define MDP5_SMP_ALLOC_R_REG_CLIENT1__MASK 0x0000ff00 +#define MDP5_SMP_ALLOC_R_REG_CLIENT1__SHIFT 8 +static inline uint32_t MDP5_SMP_ALLOC_R_REG_CLIENT1(uint32_t val) { - return ((val) << MDP5_MDP_SMP_ALLOC_R_REG_CLIENT1__SHIFT) & MDP5_MDP_SMP_ALLOC_R_REG_CLIENT1__MASK; + return ((val) << MDP5_SMP_ALLOC_R_REG_CLIENT1__SHIFT) & MDP5_SMP_ALLOC_R_REG_CLIENT1__MASK; } -#define MDP5_MDP_SMP_ALLOC_R_REG_CLIENT2__MASK 0x00ff0000 -#define MDP5_MDP_SMP_ALLOC_R_REG_CLIENT2__SHIFT 16 -static inline uint32_t MDP5_MDP_SMP_ALLOC_R_REG_CLIENT2(uint32_t val) +#define MDP5_SMP_ALLOC_R_REG_CLIENT2__MASK 0x00ff0000 +#define MDP5_SMP_ALLOC_R_REG_CLIENT2__SHIFT 16 +static inline uint32_t MDP5_SMP_ALLOC_R_REG_CLIENT2(uint32_t val) { - return ((val) << MDP5_MDP_SMP_ALLOC_R_REG_CLIENT2__SHIFT) & MDP5_MDP_SMP_ALLOC_R_REG_CLIENT2__MASK; + return ((val) << MDP5_SMP_ALLOC_R_REG_CLIENT2__SHIFT) & MDP5_SMP_ALLOC_R_REG_CLIENT2__MASK; } static inline uint32_t __offset_IGC(enum mdp5_igc_type idx) @@ -322,35 +305,35 @@ static inline uint32_t __offset_IGC(enum mdp5_igc_type idx) default: return INVALID_IDX(idx); } } -static inline uint32_t REG_MDP5_MDP_IGC(uint32_t i0, enum mdp5_igc_type i1) { return 0x00000000 + __offset_MDP(i0) + __offset_IGC(i1); } +static inline uint32_t REG_MDP5_IGC(enum mdp5_igc_type i0) { return 0x00000000 + __offset_IGC(i0); } -static inline uint32_t REG_MDP5_MDP_IGC_LUT(uint32_t i0, enum mdp5_igc_type i1, uint32_t i2) { return 0x00000000 + __offset_MDP(i0) + __offset_IGC(i1) + 0x4*i2; } +static inline uint32_t REG_MDP5_IGC_LUT(enum mdp5_igc_type i0, uint32_t i1) { return 0x00000000 + __offset_IGC(i0) + 0x4*i1; } -static inline uint32_t REG_MDP5_MDP_IGC_LUT_REG(uint32_t i0, enum mdp5_igc_type i1, uint32_t i2) { return 0x00000000 + __offset_MDP(i0) + __offset_IGC(i1) + 0x4*i2; } -#define MDP5_MDP_IGC_LUT_REG_VAL__MASK 0x00000fff -#define MDP5_MDP_IGC_LUT_REG_VAL__SHIFT 0 -static inline uint32_t MDP5_MDP_IGC_LUT_REG_VAL(uint32_t val) +static inline uint32_t REG_MDP5_IGC_LUT_REG(enum mdp5_igc_type i0, uint32_t i1) { return 0x00000000 + __offset_IGC(i0) + 0x4*i1; } +#define MDP5_IGC_LUT_REG_VAL__MASK 0x00000fff +#define MDP5_IGC_LUT_REG_VAL__SHIFT 0 +static inline uint32_t MDP5_IGC_LUT_REG_VAL(uint32_t val) { - return ((val) << MDP5_MDP_IGC_LUT_REG_VAL__SHIFT) & MDP5_MDP_IGC_LUT_REG_VAL__MASK; + return ((val) << MDP5_IGC_LUT_REG_VAL__SHIFT) & MDP5_IGC_LUT_REG_VAL__MASK; } -#define MDP5_MDP_IGC_LUT_REG_INDEX_UPDATE 0x02000000 -#define MDP5_MDP_IGC_LUT_REG_DISABLE_PIPE_0 0x10000000 -#define MDP5_MDP_IGC_LUT_REG_DISABLE_PIPE_1 0x20000000 -#define MDP5_MDP_IGC_LUT_REG_DISABLE_PIPE_2 0x40000000 +#define MDP5_IGC_LUT_REG_INDEX_UPDATE 0x02000000 +#define MDP5_IGC_LUT_REG_DISABLE_PIPE_0 0x10000000 +#define MDP5_IGC_LUT_REG_DISABLE_PIPE_1 0x20000000 +#define MDP5_IGC_LUT_REG_DISABLE_PIPE_2 0x40000000 -static inline uint32_t REG_MDP5_MDP_SPLIT_DPL_EN(uint32_t i0) { return 0x000002f4 + __offset_MDP(i0); } +#define REG_MDP5_SPLIT_DPL_EN 0x000002f4 -static inline uint32_t REG_MDP5_MDP_SPLIT_DPL_UPPER(uint32_t i0) { return 0x000002f8 + __offset_MDP(i0); } -#define MDP5_MDP_SPLIT_DPL_UPPER_SMART_PANEL 0x00000002 -#define MDP5_MDP_SPLIT_DPL_UPPER_SMART_PANEL_FREE_RUN 0x00000004 -#define MDP5_MDP_SPLIT_DPL_UPPER_INTF1_SW_TRG_MUX 0x00000010 -#define MDP5_MDP_SPLIT_DPL_UPPER_INTF2_SW_TRG_MUX 0x00000100 +#define REG_MDP5_SPLIT_DPL_UPPER 0x000002f8 +#define MDP5_SPLIT_DPL_UPPER_SMART_PANEL 0x00000002 +#define MDP5_SPLIT_DPL_UPPER_SMART_PANEL_FREE_RUN 0x00000004 +#define MDP5_SPLIT_DPL_UPPER_INTF1_SW_TRG_MUX 0x00000010 +#define MDP5_SPLIT_DPL_UPPER_INTF2_SW_TRG_MUX 0x00000100 -static inline uint32_t REG_MDP5_MDP_SPLIT_DPL_LOWER(uint32_t i0) { return 0x000003f0 + __offset_MDP(i0); } -#define MDP5_MDP_SPLIT_DPL_LOWER_SMART_PANEL 0x00000002 -#define MDP5_MDP_SPLIT_DPL_LOWER_SMART_PANEL_FREE_RUN 0x00000004 -#define MDP5_MDP_SPLIT_DPL_LOWER_INTF1_TG_SYNC 0x00000010 -#define MDP5_MDP_SPLIT_DPL_LOWER_INTF2_TG_SYNC 0x00000100 +#define REG_MDP5_SPLIT_DPL_LOWER 0x000003f0 +#define MDP5_SPLIT_DPL_LOWER_SMART_PANEL 0x00000002 +#define MDP5_SPLIT_DPL_LOWER_SMART_PANEL_FREE_RUN 0x00000004 +#define MDP5_SPLIT_DPL_LOWER_INTF1_TG_SYNC 0x00000010 +#define MDP5_SPLIT_DPL_LOWER_INTF2_TG_SYNC 0x00000100 static inline uint32_t __offset_CTL(uint32_t idx) { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c index 57f73f0c120d..ac9e4cde1380 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c @@ -26,7 +26,6 @@ const struct mdp5_cfg_hw msm8x74v1_config = { .name = "msm8x74v1", .mdp = { .count = 1, - .base = { 0x00100 }, .caps = MDP_CAP_SMP | 0, }, @@ -41,12 +40,12 @@ const struct mdp5_cfg_hw msm8x74v1_config = { }, .ctl = { .count = 5, - .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 }, + .base = { 0x00500, 0x00600, 0x00700, 0x00800, 0x00900 }, .flush_hw_mask = 0x0003ffff, }, .pipe_vig = { .count = 3, - .base = { 0x01200, 0x01600, 0x01a00 }, + .base = { 0x01100, 0x01500, 0x01900 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | @@ -55,7 +54,7 @@ const struct mdp5_cfg_hw msm8x74v1_config = { }, .pipe_rgb = { .count = 3, - .base = { 0x01e00, 0x02200, 0x02600 }, + .base = { 0x01d00, 0x02100, 0x02500 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | @@ -63,26 +62,26 @@ const struct mdp5_cfg_hw msm8x74v1_config = { }, .pipe_dma = { .count = 2, - .base = { 0x02a00, 0x02e00 }, + .base = { 0x02900, 0x02d00 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | 0, }, .lm = { .count = 5, - .base = { 0x03200, 0x03600, 0x03a00, 0x03e00, 0x04200 }, + .base = { 0x03100, 0x03500, 0x03900, 0x03d00, 0x04100 }, .nb_stages = 5, }, .dspp = { .count = 3, - .base = { 0x04600, 0x04a00, 0x04e00 }, + .base = { 0x04500, 0x04900, 0x04d00 }, }, .pp = { .count = 3, - .base = { 0x21b00, 0x21c00, 0x21d00 }, + .base = { 0x21a00, 0x21b00, 0x21c00 }, }, .intf = { - .base = { 0x21100, 0x21300, 0x21500, 0x21700 }, + .base = { 0x21000, 0x21200, 0x21400, 0x21600 }, .connect = { [0] = INTF_eDP, [1] = INTF_DSI, @@ -97,7 +96,6 @@ const struct mdp5_cfg_hw msm8x74v2_config = { .name = "msm8x74", .mdp = { .count = 1, - .base = { 0x00100 }, .caps = MDP_CAP_SMP | 0, }, @@ -112,48 +110,48 @@ const struct mdp5_cfg_hw msm8x74v2_config = { }, .ctl = { .count = 5, - .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 }, + .base = { 0x00500, 0x00600, 0x00700, 0x00800, 0x00900 }, .flush_hw_mask = 0x0003ffff, }, .pipe_vig = { .count = 3, - .base = { 0x01200, 0x01600, 0x01a00 }, + .base = { 0x01100, 0x01500, 0x01900 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC | MDP_PIPE_CAP_DECIMATION, }, .pipe_rgb = { .count = 3, - .base = { 0x01e00, 0x02200, 0x02600 }, + .base = { 0x01d00, 0x02100, 0x02500 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_DECIMATION, }, .pipe_dma = { .count = 2, - .base = { 0x02a00, 0x02e00 }, + .base = { 0x02900, 0x02d00 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP, }, .lm = { .count = 5, - .base = { 0x03200, 0x03600, 0x03a00, 0x03e00, 0x04200 }, + .base = { 0x03100, 0x03500, 0x03900, 0x03d00, 0x04100 }, .nb_stages = 5, .max_width = 2048, .max_height = 0xFFFF, }, .dspp = { .count = 3, - .base = { 0x04600, 0x04a00, 0x04e00 }, + .base = { 0x04500, 0x04900, 0x04d00 }, }, .ad = { .count = 2, - .base = { 0x13100, 0x13300 }, + .base = { 0x13000, 0x13200 }, }, .pp = { .count = 3, - .base = { 0x12d00, 0x12e00, 0x12f00 }, + .base = { 0x12c00, 0x12d00, 0x12e00 }, }, .intf = { - .base = { 0x12500, 0x12700, 0x12900, 0x12b00 }, + .base = { 0x12400, 0x12600, 0x12800, 0x12a00 }, .connect = { [0] = INTF_eDP, [1] = INTF_DSI, @@ -168,7 +166,6 @@ const struct mdp5_cfg_hw apq8084_config = { .name = "apq8084", .mdp = { .count = 1, - .base = { 0x00100 }, .caps = MDP_CAP_SMP | 0, }, @@ -190,49 +187,49 @@ const struct mdp5_cfg_hw apq8084_config = { }, .ctl = { .count = 5, - .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 }, + .base = { 0x00500, 0x00600, 0x00700, 0x00800, 0x00900 }, .flush_hw_mask = 0x003fffff, }, .pipe_vig = { .count = 4, - .base = { 0x01200, 0x01600, 0x01a00, 0x01e00 }, + .base = { 0x01100, 0x01500, 0x01900, 0x01d00 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC | MDP_PIPE_CAP_DECIMATION, }, .pipe_rgb = { .count = 4, - .base = { 0x02200, 0x02600, 0x02a00, 0x02e00 }, + .base = { 0x02100, 0x02500, 0x02900, 0x02d00 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_DECIMATION, }, .pipe_dma = { .count = 2, - .base = { 0x03200, 0x03600 }, + .base = { 0x03100, 0x03500 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP, }, .lm = { .count = 6, - .base = { 0x03a00, 0x03e00, 0x04200, 0x04600, 0x04a00, 0x04e00 }, + .base = { 0x03900, 0x03d00, 0x04100, 0x04500, 0x04900, 0x04d00 }, .nb_stages = 5, .max_width = 2048, .max_height = 0xFFFF, }, .dspp = { .count = 4, - .base = { 0x05200, 0x05600, 0x05a00, 0x05e00 }, + .base = { 0x05100, 0x05500, 0x05900, 0x05d00 }, }, .ad = { .count = 3, - .base = { 0x13500, 0x13700, 0x13900 }, + .base = { 0x13400, 0x13600, 0x13800 }, }, .pp = { .count = 4, - .base = { 0x12f00, 0x13000, 0x13100, 0x13200 }, + .base = { 0x12e00, 0x12f00, 0x13000, 0x13100 }, }, .intf = { - .base = { 0x12500, 0x12700, 0x12900, 0x12b00, 0x12d00 }, + .base = { 0x12400, 0x12600, 0x12800, 0x12a00, 0x12c00 }, .connect = { [0] = INTF_eDP, [1] = INTF_DSI, @@ -247,7 +244,7 @@ const struct mdp5_cfg_hw msm8x16_config = { .name = "msm8x16", .mdp = { .count = 1, - .base = { 0x01000 }, + .base = { 0x0 }, .caps = MDP_CAP_SMP | 0, }, @@ -261,41 +258,41 @@ const struct mdp5_cfg_hw msm8x16_config = { }, .ctl = { .count = 5, - .base = { 0x02000, 0x02200, 0x02400, 0x02600, 0x02800 }, + .base = { 0x01000, 0x01200, 0x01400, 0x01600, 0x01800 }, .flush_hw_mask = 0x4003ffff, }, .pipe_vig = { .count = 1, - .base = { 0x05000 }, + .base = { 0x04000 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC | MDP_PIPE_CAP_DECIMATION, }, .pipe_rgb = { .count = 2, - .base = { 0x15000, 0x17000 }, + .base = { 0x14000, 0x16000 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_DECIMATION, }, .pipe_dma = { .count = 1, - .base = { 0x25000 }, + .base = { 0x24000 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP, }, .lm = { .count = 2, /* LM0 and LM3 */ - .base = { 0x45000, 0x48000 }, + .base = { 0x44000, 0x47000 }, .nb_stages = 5, .max_width = 2048, .max_height = 0xFFFF, }, .dspp = { .count = 1, - .base = { 0x55000 }, + .base = { 0x54000 }, }, .intf = { - .base = { 0x00000, 0x6b800 }, + .base = { 0x00000, 0x6a800 }, .connect = { [0] = INTF_DISABLED, [1] = INTF_DSI, @@ -308,7 +305,6 @@ const struct mdp5_cfg_hw msm8x94_config = { .name = "msm8x94", .mdp = { .count = 1, - .base = { 0x01000 }, .caps = MDP_CAP_SMP | 0, }, @@ -330,49 +326,49 @@ const struct mdp5_cfg_hw msm8x94_config = { }, .ctl = { .count = 5, - .base = { 0x02000, 0x02200, 0x02400, 0x02600, 0x02800 }, + .base = { 0x01000, 0x01200, 0x01400, 0x01600, 0x01800 }, .flush_hw_mask = 0xf0ffffff, }, .pipe_vig = { .count = 4, - .base = { 0x05000, 0x07000, 0x09000, 0x0b000 }, + .base = { 0x04000, 0x06000, 0x08000, 0x0a000 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC | MDP_PIPE_CAP_DECIMATION, }, .pipe_rgb = { .count = 4, - .base = { 0x15000, 0x17000, 0x19000, 0x1b000 }, + .base = { 0x14000, 0x16000, 0x18000, 0x1a000 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_DECIMATION, }, .pipe_dma = { .count = 2, - .base = { 0x25000, 0x27000 }, + .base = { 0x24000, 0x26000 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP, }, .lm = { .count = 6, - .base = { 0x45000, 0x46000, 0x47000, 0x48000, 0x49000, 0x4a000 }, + .base = { 0x44000, 0x45000, 0x46000, 0x47000, 0x48000, 0x49000 }, .nb_stages = 8, .max_width = 2048, .max_height = 0xFFFF, }, .dspp = { .count = 4, - .base = { 0x55000, 0x57000, 0x59000, 0x5b000 }, + .base = { 0x54000, 0x56000, 0x58000, 0x5a000 }, }, .ad = { .count = 3, - .base = { 0x79000, 0x79800, 0x7a000 }, + .base = { 0x78000, 0x78800, 0x79000 }, }, .pp = { .count = 4, - .base = { 0x71000, 0x71800, 0x72000, 0x72800 }, + .base = { 0x70000, 0x70800, 0x71000, 0x71800 }, }, .intf = { - .base = { 0x6b000, 0x6b800, 0x6c000, 0x6c800, 0x6d000 }, + .base = { 0x6a000, 0x6a800, 0x6b000, 0x6b800, 0x6c000 }, .connect = { [0] = INTF_DISABLED, [1] = INTF_DSI, @@ -387,19 +383,18 @@ const struct mdp5_cfg_hw msm8x96_config = { .name = "msm8x96", .mdp = { .count = 1, - .base = { 0x01000 }, .caps = MDP_CAP_DSC | MDP_CAP_CDM | 0, }, .ctl = { .count = 5, - .base = { 0x02000, 0x02200, 0x02400, 0x02600, 0x02800 }, + .base = { 0x01000, 0x01200, 0x01400, 0x01600, 0x01800 }, .flush_hw_mask = 0xf4ffffff, }, .pipe_vig = { .count = 4, - .base = { 0x05000, 0x07000, 0x09000, 0x0b000 }, + .base = { 0x04000, 0x06000, 0x08000, 0x0a000 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | @@ -410,7 +405,7 @@ const struct mdp5_cfg_hw msm8x96_config = { }, .pipe_rgb = { .count = 4, - .base = { 0x15000, 0x17000, 0x19000, 0x1b000 }, + .base = { 0x14000, 0x16000, 0x18000, 0x1a000 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SCALE | @@ -420,7 +415,7 @@ const struct mdp5_cfg_hw msm8x96_config = { }, .pipe_dma = { .count = 2, - .base = { 0x25000, 0x27000 }, + .base = { 0x24000, 0x26000 }, .caps = MDP_PIPE_CAP_HFLIP | MDP_PIPE_CAP_VFLIP | MDP_PIPE_CAP_SW_PIX_EXT | @@ -428,33 +423,33 @@ const struct mdp5_cfg_hw msm8x96_config = { }, .lm = { .count = 6, - .base = { 0x45000, 0x46000, 0x47000, 0x48000, 0x49000, 0x4a000 }, + .base = { 0x44000, 0x45000, 0x46000, 0x47000, 0x48000, 0x49000 }, .nb_stages = 8, .max_width = 2560, .max_height = 0xFFFF, }, .dspp = { .count = 2, - .base = { 0x55000, 0x57000 }, + .base = { 0x54000, 0x56000 }, }, .ad = { .count = 3, - .base = { 0x79000, 0x79800, 0x7a000 }, + .base = { 0x78000, 0x78800, 0x79000 }, }, .pp = { .count = 4, - .base = { 0x71000, 0x71800, 0x72000, 0x72800 }, + .base = { 0x70000, 0x70800, 0x71000, 0x71800 }, }, .cdm = { .count = 1, - .base = { 0x7a200 }, + .base = { 0x79200 }, }, .dsc = { .count = 2, - .base = { 0x81000, 0x81400 }, + .base = { 0x80000, 0x80400 }, }, .intf = { - .base = { 0x6b000, 0x6b800, 0x6c000, 0x6c800, 0x6d000 }, + .base = { 0x6a000, 0x6a800, 0x6b000, 0x6b800, 0x6c000 }, .connect = { [0] = INTF_DISABLED, [1] = INTF_DSI, diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c index 69094cb28103..c627ab6d0061 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c @@ -272,22 +272,22 @@ int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder, * start signal for the slave encoder */ if (intf_num == 1) - data |= MDP5_MDP_SPLIT_DPL_UPPER_INTF2_SW_TRG_MUX; + data |= MDP5_SPLIT_DPL_UPPER_INTF2_SW_TRG_MUX; else if (intf_num == 2) - data |= MDP5_MDP_SPLIT_DPL_UPPER_INTF1_SW_TRG_MUX; + data |= MDP5_SPLIT_DPL_UPPER_INTF1_SW_TRG_MUX; else return -EINVAL; /* Smart Panel, Sync mode */ - data |= MDP5_MDP_SPLIT_DPL_UPPER_SMART_PANEL; + data |= MDP5_SPLIT_DPL_UPPER_SMART_PANEL; /* Make sure clocks are on when connectors calling this function. */ mdp5_enable(mdp5_kms); - mdp5_write(mdp5_kms, REG_MDP5_MDP_SPLIT_DPL_UPPER(0), data); + mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, data); - mdp5_write(mdp5_kms, REG_MDP5_MDP_SPLIT_DPL_LOWER(0), - MDP5_MDP_SPLIT_DPL_LOWER_SMART_PANEL); - mdp5_write(mdp5_kms, REG_MDP5_MDP_SPLIT_DPL_EN(0), 1); + mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER, + MDP5_SPLIT_DPL_LOWER_SMART_PANEL); + mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1); mdp5_disable(mdp5_kms); return 0; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 4e8ed739f558..fa2be7ce9468 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -490,8 +490,7 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, struct mdp5_kms *mdp5_kms = get_kms(crtc); struct drm_gem_object *cursor_bo, *old_bo = NULL; uint32_t blendcfg, cursor_addr, stride; - int ret, bpp, lm; - unsigned int depth; + int ret, lm; enum mdp5_cursor_alpha cur_alpha = CURSOR_ALPHA_PER_PIXEL; uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0); uint32_t roi_w, roi_h; @@ -521,8 +520,7 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, return -EINVAL; lm = mdp5_crtc->lm; - drm_fb_get_bpp_depth(DRM_FORMAT_ARGB8888, &depth, &bpp); - stride = width * (bpp >> 3); + stride = width * drm_format_plane_cpp(DRM_FORMAT_ARGB8888, 0); spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags); old_bo = mdp5_crtc->cursor.scanout_bo; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c index 4e81ca4f964a..d021edc3b307 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c @@ -118,31 +118,31 @@ static void set_display_intf(struct mdp5_kms *mdp5_kms, u32 intf_sel; spin_lock_irqsave(&mdp5_kms->resource_lock, flags); - intf_sel = mdp5_read(mdp5_kms, REG_MDP5_MDP_DISP_INTF_SEL(0)); + intf_sel = mdp5_read(mdp5_kms, REG_MDP5_DISP_INTF_SEL); switch (intf->num) { case 0: - intf_sel &= ~MDP5_MDP_DISP_INTF_SEL_INTF0__MASK; - intf_sel |= MDP5_MDP_DISP_INTF_SEL_INTF0(intf->type); + intf_sel &= ~MDP5_DISP_INTF_SEL_INTF0__MASK; + intf_sel |= MDP5_DISP_INTF_SEL_INTF0(intf->type); break; case 1: - intf_sel &= ~MDP5_MDP_DISP_INTF_SEL_INTF1__MASK; - intf_sel |= MDP5_MDP_DISP_INTF_SEL_INTF1(intf->type); + intf_sel &= ~MDP5_DISP_INTF_SEL_INTF1__MASK; + intf_sel |= MDP5_DISP_INTF_SEL_INTF1(intf->type); break; case 2: - intf_sel &= ~MDP5_MDP_DISP_INTF_SEL_INTF2__MASK; - intf_sel |= MDP5_MDP_DISP_INTF_SEL_INTF2(intf->type); + intf_sel &= ~MDP5_DISP_INTF_SEL_INTF2__MASK; + intf_sel |= MDP5_DISP_INTF_SEL_INTF2(intf->type); break; case 3: - intf_sel &= ~MDP5_MDP_DISP_INTF_SEL_INTF3__MASK; - intf_sel |= MDP5_MDP_DISP_INTF_SEL_INTF3(intf->type); + intf_sel &= ~MDP5_DISP_INTF_SEL_INTF3__MASK; + intf_sel |= MDP5_DISP_INTF_SEL_INTF3(intf->type); break; default: BUG(); break; } - mdp5_write(mdp5_kms, REG_MDP5_MDP_DISP_INTF_SEL(0), intf_sel); + mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, intf_sel); spin_unlock_irqrestore(&mdp5_kms->resource_lock, flags); } @@ -557,7 +557,7 @@ int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable) if (!enable) { ctlx->pair = NULL; ctly->pair = NULL; - mdp5_write(mdp5_kms, REG_MDP5_MDP_SPARE_0(0), 0); + mdp5_write(mdp5_kms, REG_MDP5_SPARE_0, 0); return 0; } else if ((ctlx->pair != NULL) || (ctly->pair != NULL)) { dev_err(ctl_mgr->dev->dev, "CTLs already paired\n"); @@ -570,8 +570,8 @@ int mdp5_ctl_pair(struct mdp5_ctl *ctlx, struct mdp5_ctl *ctly, bool enable) ctlx->pair = ctly; ctly->pair = ctlx; - mdp5_write(mdp5_kms, REG_MDP5_MDP_SPARE_0(0), - MDP5_MDP_SPARE_0_SPLIT_DPL_SINGLE_FLUSH_EN); + mdp5_write(mdp5_kms, REG_MDP5_SPARE_0, + MDP5_SPARE_0_SPLIT_DPL_SINGLE_FLUSH_EN); return 0; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c index 1d95f9fd9dc7..fe0c22230883 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c @@ -322,18 +322,18 @@ int mdp5_encoder_set_split_display(struct drm_encoder *encoder, * to use the master's enable signal for the slave encoder. */ if (intf_num == 1) - data |= MDP5_MDP_SPLIT_DPL_LOWER_INTF2_TG_SYNC; + data |= MDP5_SPLIT_DPL_LOWER_INTF2_TG_SYNC; else if (intf_num == 2) - data |= MDP5_MDP_SPLIT_DPL_LOWER_INTF1_TG_SYNC; + data |= MDP5_SPLIT_DPL_LOWER_INTF1_TG_SYNC; else return -EINVAL; /* Make sure clocks are on when connectors calling this function. */ mdp5_enable(mdp5_kms); /* Dumb Panel, Sync mode */ - mdp5_write(mdp5_kms, REG_MDP5_MDP_SPLIT_DPL_UPPER(0), 0); - mdp5_write(mdp5_kms, REG_MDP5_MDP_SPLIT_DPL_LOWER(0), data); - mdp5_write(mdp5_kms, REG_MDP5_MDP_SPLIT_DPL_EN(0), 1); + mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, 0); + mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER, data); + mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1); mdp5_ctl_pair(mdp5_encoder->ctl, mdp5_slave_enc->ctl, true); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c index 73bc3e312fd4..d53e5510fd7c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_irq.c @@ -15,7 +15,6 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <linux/irqdomain.h> #include <linux/irq.h> #include "msm_drv.h" @@ -24,9 +23,9 @@ void mdp5_set_irqmask(struct mdp_kms *mdp_kms, uint32_t irqmask, uint32_t old_irqmask) { - mdp5_write(to_mdp5_kms(mdp_kms), REG_MDP5_MDP_INTR_CLEAR(0), - irqmask ^ (irqmask & old_irqmask)); - mdp5_write(to_mdp5_kms(mdp_kms), REG_MDP5_MDP_INTR_EN(0), irqmask); + mdp5_write(to_mdp5_kms(mdp_kms), REG_MDP5_INTR_CLEAR, + irqmask ^ (irqmask & old_irqmask)); + mdp5_write(to_mdp5_kms(mdp_kms), REG_MDP5_INTR_EN, irqmask); } static void mdp5_irq_error_handler(struct mdp_irq *irq, uint32_t irqstatus) @@ -38,8 +37,8 @@ void mdp5_irq_preinstall(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); mdp5_enable(mdp5_kms); - mdp5_write(mdp5_kms, REG_MDP5_MDP_INTR_CLEAR(0), 0xffffffff); - mdp5_write(mdp5_kms, REG_MDP5_MDP_INTR_EN(0), 0x00000000); + mdp5_write(mdp5_kms, REG_MDP5_INTR_CLEAR, 0xffffffff); + mdp5_write(mdp5_kms, REG_MDP5_INTR_EN, 0x00000000); mdp5_disable(mdp5_kms); } @@ -55,7 +54,9 @@ int mdp5_irq_postinstall(struct msm_kms *kms) MDP5_IRQ_INTF2_UNDER_RUN | MDP5_IRQ_INTF3_UNDER_RUN; + mdp5_enable(mdp5_kms); mdp_irq_register(mdp_kms, error_handler); + mdp5_disable(mdp5_kms); return 0; } @@ -64,21 +65,22 @@ void mdp5_irq_uninstall(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); mdp5_enable(mdp5_kms); - mdp5_write(mdp5_kms, REG_MDP5_MDP_INTR_EN(0), 0x00000000); + mdp5_write(mdp5_kms, REG_MDP5_INTR_EN, 0x00000000); mdp5_disable(mdp5_kms); } -static void mdp5_irq_mdp(struct mdp_kms *mdp_kms) +irqreturn_t mdp5_irq(struct msm_kms *kms) { + struct mdp_kms *mdp_kms = to_mdp_kms(kms); struct mdp5_kms *mdp5_kms = to_mdp5_kms(mdp_kms); struct drm_device *dev = mdp5_kms->dev; struct msm_drm_private *priv = dev->dev_private; unsigned int id; uint32_t status, enable; - enable = mdp5_read(mdp5_kms, REG_MDP5_MDP_INTR_EN(0)); - status = mdp5_read(mdp5_kms, REG_MDP5_MDP_INTR_STATUS(0)) & enable; - mdp5_write(mdp5_kms, REG_MDP5_MDP_INTR_CLEAR(0), status); + enable = mdp5_read(mdp5_kms, REG_MDP5_INTR_EN); + status = mdp5_read(mdp5_kms, REG_MDP5_INTR_STATUS) & enable; + mdp5_write(mdp5_kms, REG_MDP5_INTR_CLEAR, status); VERB("status=%08x", status); @@ -87,29 +89,6 @@ static void mdp5_irq_mdp(struct mdp_kms *mdp_kms) for (id = 0; id < priv->num_crtcs; id++) if (status & mdp5_crtc_vblank(priv->crtcs[id])) drm_handle_vblank(dev, id); -} - -irqreturn_t mdp5_irq(struct msm_kms *kms) -{ - struct mdp_kms *mdp_kms = to_mdp_kms(kms); - struct mdp5_kms *mdp5_kms = to_mdp5_kms(mdp_kms); - uint32_t intr; - - intr = mdp5_read(mdp5_kms, REG_MDSS_HW_INTR_STATUS); - - VERB("intr=%08x", intr); - - if (intr & MDSS_HW_INTR_STATUS_INTR_MDP) { - mdp5_irq_mdp(mdp_kms); - intr &= ~MDSS_HW_INTR_STATUS_INTR_MDP; - } - - while (intr) { - irq_hw_number_t hwirq = fls(intr) - 1; - generic_handle_irq(irq_find_mapping( - mdp5_kms->irqcontroller.domain, hwirq)); - intr &= ~(1 << hwirq); - } return IRQ_HANDLED; } @@ -135,81 +114,3 @@ void mdp5_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) mdp5_crtc_vblank(crtc), false); mdp5_disable(mdp5_kms); } - -/* - * interrupt-controller implementation, so sub-blocks (hdmi/eDP/dsi/etc) - * can register to get their irq's delivered - */ - -#define VALID_IRQS (MDSS_HW_INTR_STATUS_INTR_DSI0 | \ - MDSS_HW_INTR_STATUS_INTR_DSI1 | \ - MDSS_HW_INTR_STATUS_INTR_HDMI | \ - MDSS_HW_INTR_STATUS_INTR_EDP) - -static void mdp5_hw_mask_irq(struct irq_data *irqd) -{ - struct mdp5_kms *mdp5_kms = irq_data_get_irq_chip_data(irqd); - smp_mb__before_atomic(); - clear_bit(irqd->hwirq, &mdp5_kms->irqcontroller.enabled_mask); - smp_mb__after_atomic(); -} - -static void mdp5_hw_unmask_irq(struct irq_data *irqd) -{ - struct mdp5_kms *mdp5_kms = irq_data_get_irq_chip_data(irqd); - smp_mb__before_atomic(); - set_bit(irqd->hwirq, &mdp5_kms->irqcontroller.enabled_mask); - smp_mb__after_atomic(); -} - -static struct irq_chip mdp5_hw_irq_chip = { - .name = "mdp5", - .irq_mask = mdp5_hw_mask_irq, - .irq_unmask = mdp5_hw_unmask_irq, -}; - -static int mdp5_hw_irqdomain_map(struct irq_domain *d, - unsigned int irq, irq_hw_number_t hwirq) -{ - struct mdp5_kms *mdp5_kms = d->host_data; - - if (!(VALID_IRQS & (1 << hwirq))) - return -EPERM; - - irq_set_chip_and_handler(irq, &mdp5_hw_irq_chip, handle_level_irq); - irq_set_chip_data(irq, mdp5_kms); - - return 0; -} - -static struct irq_domain_ops mdp5_hw_irqdomain_ops = { - .map = mdp5_hw_irqdomain_map, - .xlate = irq_domain_xlate_onecell, -}; - - -int mdp5_irq_domain_init(struct mdp5_kms *mdp5_kms) -{ - struct device *dev = mdp5_kms->dev->dev; - struct irq_domain *d; - - d = irq_domain_add_linear(dev->of_node, 32, - &mdp5_hw_irqdomain_ops, mdp5_kms); - if (!d) { - dev_err(dev, "mdp5 irq domain add failed\n"); - return -ENXIO; - } - - mdp5_kms->irqcontroller.enabled_mask = 0; - mdp5_kms->irqcontroller.domain = d; - - return 0; -} - -void mdp5_irq_domain_fini(struct mdp5_kms *mdp5_kms) -{ - if (mdp5_kms->irqcontroller.domain) { - irq_domain_remove(mdp5_kms->irqcontroller.domain); - mdp5_kms->irqcontroller.domain = NULL; - } -} diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index f0c285b1c027..ed7143d35b25 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -16,6 +16,7 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <linux/of_irq.h> #include "msm_drv.h" #include "msm_mmu.h" @@ -28,10 +29,11 @@ static const char *iommu_ports[] = { static int mdp5_hw_init(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); - struct drm_device *dev = mdp5_kms->dev; + struct platform_device *pdev = mdp5_kms->pdev; unsigned long flags; - pm_runtime_get_sync(dev->dev); + pm_runtime_get_sync(&pdev->dev); + mdp5_enable(mdp5_kms); /* Magic unknown register writes: * @@ -58,12 +60,13 @@ static int mdp5_hw_init(struct msm_kms *kms) */ spin_lock_irqsave(&mdp5_kms->resource_lock, flags); - mdp5_write(mdp5_kms, REG_MDP5_MDP_DISP_INTF_SEL(0), 0); + mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, 0); spin_unlock_irqrestore(&mdp5_kms->resource_lock, flags); mdp5_ctlm_hw_reset(mdp5_kms->ctlm); - pm_runtime_put_sync(dev->dev); + mdp5_disable(mdp5_kms); + pm_runtime_put_sync(&pdev->dev); return 0; } @@ -111,26 +114,15 @@ static int mdp5_set_split_display(struct msm_kms *kms, return mdp5_encoder_set_split_display(encoder, slave_encoder); } -static void mdp5_destroy(struct msm_kms *kms) +static void mdp5_kms_destroy(struct msm_kms *kms) { struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); struct msm_mmu *mmu = mdp5_kms->mmu; - mdp5_irq_domain_fini(mdp5_kms); - if (mmu) { mmu->funcs->detach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); mmu->funcs->destroy(mmu); } - - if (mdp5_kms->ctlm) - mdp5_ctlm_destroy(mdp5_kms->ctlm); - if (mdp5_kms->smp) - mdp5_smp_destroy(mdp5_kms->smp); - if (mdp5_kms->cfg) - mdp5_cfg_destroy(mdp5_kms->cfg); - - kfree(mdp5_kms); } static const struct mdp_kms_funcs kms_funcs = { @@ -148,7 +140,7 @@ static const struct mdp_kms_funcs kms_funcs = { .get_format = mdp_get_format, .round_pixclk = mdp5_round_pixclk, .set_split_display = mdp5_set_split_display, - .destroy = mdp5_destroy, + .destroy = mdp5_kms_destroy, }, .set_irqmask = mdp5_set_irqmask, }; @@ -345,13 +337,6 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); - /* register our interrupt-controller for hdmi/eDP/dsi/etc - * to use for irqs routed through mdp: - */ - ret = mdp5_irq_domain_init(mdp5_kms); - if (ret) - goto fail; - /* construct CRTCs and their private planes: */ for (i = 0; i < hw_cfg->pipe_rgb.count; i++) { struct drm_plane *plane; @@ -419,17 +404,17 @@ fail: return ret; } -static void read_hw_revision(struct mdp5_kms *mdp5_kms, - uint32_t *major, uint32_t *minor) +static void read_mdp_hw_revision(struct mdp5_kms *mdp5_kms, + u32 *major, u32 *minor) { - uint32_t version; + u32 version; mdp5_enable(mdp5_kms); - version = mdp5_read(mdp5_kms, REG_MDSS_HW_VERSION); + version = mdp5_read(mdp5_kms, REG_MDP5_HW_VERSION); mdp5_disable(mdp5_kms); - *major = FIELD(version, MDSS_HW_VERSION_MAJOR); - *minor = FIELD(version, MDSS_HW_VERSION_MINOR); + *major = FIELD(version, MDP5_HW_VERSION_MAJOR); + *minor = FIELD(version, MDP5_HW_VERSION_MINOR); DBG("MDP5 version v%d.%d", *major, *minor); } @@ -574,51 +559,146 @@ static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe) struct msm_kms *mdp5_kms_init(struct drm_device *dev) { - struct platform_device *pdev = dev->platformdev; - struct mdp5_cfg *config; + struct msm_drm_private *priv = dev->dev_private; + struct platform_device *pdev; struct mdp5_kms *mdp5_kms; - struct msm_kms *kms = NULL; + struct mdp5_cfg *config; + struct msm_kms *kms; struct msm_mmu *mmu; - uint32_t major, minor; - int i, ret; + int irq, i, ret; - mdp5_kms = kzalloc(sizeof(*mdp5_kms), GFP_KERNEL); - if (!mdp5_kms) { - dev_err(dev->dev, "failed to allocate kms\n"); - ret = -ENOMEM; + /* priv->kms would have been populated by the MDP5 driver */ + kms = priv->kms; + if (!kms) + return NULL; + + mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); + + mdp_kms_init(&mdp5_kms->base, &kms_funcs); + + pdev = mdp5_kms->pdev; + + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (irq < 0) { + ret = irq; + dev_err(&pdev->dev, "failed to get irq: %d\n", ret); goto fail; } - spin_lock_init(&mdp5_kms->resource_lock); + kms->irq = irq; - mdp_kms_init(&mdp5_kms->base, &kms_funcs); + config = mdp5_cfg_get_config(mdp5_kms->cfg); - kms = &mdp5_kms->base.base; + /* make sure things are off before attaching iommu (bootloader could + * have left things on, in which case we'll start getting faults if + * we don't disable): + */ + mdp5_enable(mdp5_kms); + for (i = 0; i < MDP5_INTF_NUM_MAX; i++) { + if (mdp5_cfg_intf_is_virtual(config->hw->intf.connect[i]) || + !config->hw->intf.base[i]) + continue; + mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0); - mdp5_kms->dev = dev; + mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(i), 0x3); + } + mdp5_disable(mdp5_kms); + mdelay(16); - /* mdp5_kms->mmio actually represents the MDSS base address */ - mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5"); - if (IS_ERR(mdp5_kms->mmio)) { - ret = PTR_ERR(mdp5_kms->mmio); + if (config->platform.iommu) { + mmu = msm_iommu_new(&pdev->dev, config->platform.iommu); + if (IS_ERR(mmu)) { + ret = PTR_ERR(mmu); + dev_err(&pdev->dev, "failed to init iommu: %d\n", ret); + iommu_domain_free(config->platform.iommu); + goto fail; + } + + ret = mmu->funcs->attach(mmu, iommu_ports, + ARRAY_SIZE(iommu_ports)); + if (ret) { + dev_err(&pdev->dev, "failed to attach iommu: %d\n", + ret); + mmu->funcs->destroy(mmu); + goto fail; + } + } else { + dev_info(&pdev->dev, + "no iommu, fallback to phys contig buffers for scanout\n"); + mmu = NULL; + } + mdp5_kms->mmu = mmu; + + mdp5_kms->id = msm_register_mmu(dev, mmu); + if (mdp5_kms->id < 0) { + ret = mdp5_kms->id; + dev_err(&pdev->dev, "failed to register mdp5 iommu: %d\n", ret); goto fail; } - mdp5_kms->vbif = msm_ioremap(pdev, "vbif_phys", "VBIF"); - if (IS_ERR(mdp5_kms->vbif)) { - ret = PTR_ERR(mdp5_kms->vbif); + ret = modeset_init(mdp5_kms); + if (ret) { + dev_err(&pdev->dev, "modeset_init failed: %d\n", ret); goto fail; } - mdp5_kms->vdd = devm_regulator_get(&pdev->dev, "vdd"); - if (IS_ERR(mdp5_kms->vdd)) { - ret = PTR_ERR(mdp5_kms->vdd); + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = config->hw->lm.max_width; + dev->mode_config.max_height = config->hw->lm.max_height; + + dev->driver->get_vblank_timestamp = mdp5_get_vblank_timestamp; + dev->driver->get_scanout_position = mdp5_get_scanoutpos; + dev->driver->get_vblank_counter = mdp5_get_vblank_counter; + dev->max_vblank_count = 0xffffffff; + dev->vblank_disable_immediate = true; + + return kms; +fail: + if (kms) + mdp5_kms_destroy(kms); + return ERR_PTR(ret); +} + +static void mdp5_destroy(struct platform_device *pdev) +{ + struct mdp5_kms *mdp5_kms = platform_get_drvdata(pdev); + + if (mdp5_kms->ctlm) + mdp5_ctlm_destroy(mdp5_kms->ctlm); + if (mdp5_kms->smp) + mdp5_smp_destroy(mdp5_kms->smp); + if (mdp5_kms->cfg) + mdp5_cfg_destroy(mdp5_kms->cfg); + + if (mdp5_kms->rpm_enabled) + pm_runtime_disable(&pdev->dev); +} + +static int mdp5_init(struct platform_device *pdev, struct drm_device *dev) +{ + struct msm_drm_private *priv = dev->dev_private; + struct mdp5_kms *mdp5_kms; + struct mdp5_cfg *config; + u32 major, minor; + int ret; + + mdp5_kms = devm_kzalloc(&pdev->dev, sizeof(*mdp5_kms), GFP_KERNEL); + if (!mdp5_kms) { + ret = -ENOMEM; goto fail; } - ret = regulator_enable(mdp5_kms->vdd); - if (ret) { - dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret); + platform_set_drvdata(pdev, mdp5_kms); + + spin_lock_init(&mdp5_kms->resource_lock); + + mdp5_kms->dev = dev; + mdp5_kms->pdev = pdev; + + mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5"); + if (IS_ERR(mdp5_kms->mmio)) { + ret = PTR_ERR(mdp5_kms->mmio); goto fail; } @@ -629,9 +709,6 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk", true); if (ret) goto fail; - ret = get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src", true); - if (ret) - goto fail; ret = get_clk(pdev, &mdp5_kms->core_clk, "core_clk", true); if (ret) goto fail; @@ -646,9 +723,12 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) * rate first, then figure out hw revision, and then set a * more optimal rate: */ - clk_set_rate(mdp5_kms->src_clk, 200000000); + clk_set_rate(mdp5_kms->core_clk, 200000000); + + pm_runtime_enable(&pdev->dev); + mdp5_kms->rpm_enabled = true; - read_hw_revision(mdp5_kms, &major, &minor); + read_mdp_hw_revision(mdp5_kms, &major, &minor); mdp5_kms->cfg = mdp5_cfg_init(mdp5_kms, major, minor); if (IS_ERR(mdp5_kms->cfg)) { @@ -661,7 +741,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) mdp5_kms->caps = config->hw->mdp.caps; /* TODO: compute core clock rate at runtime */ - clk_set_rate(mdp5_kms->src_clk, config->hw->max_clk); + clk_set_rate(mdp5_kms->core_clk, config->hw->max_clk); /* * Some chipsets have a Shared Memory Pool (SMP), while others @@ -684,73 +764,76 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev) goto fail; } - /* make sure things are off before attaching iommu (bootloader could - * have left things on, in which case we'll start getting faults if - * we don't disable): - */ - mdp5_enable(mdp5_kms); - for (i = 0; i < MDP5_INTF_NUM_MAX; i++) { - if (mdp5_cfg_intf_is_virtual(config->hw->intf.connect[i]) || - !config->hw->intf.base[i]) - continue; - mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0); + /* set uninit-ed kms */ + priv->kms = &mdp5_kms->base.base; - mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(i), 0x3); - } - mdp5_disable(mdp5_kms); - mdelay(16); + return 0; +fail: + mdp5_destroy(pdev); + return ret; +} - if (config->platform.iommu) { - mmu = msm_iommu_new(&pdev->dev, config->platform.iommu); - if (IS_ERR(mmu)) { - ret = PTR_ERR(mmu); - dev_err(dev->dev, "failed to init iommu: %d\n", ret); - iommu_domain_free(config->platform.iommu); - goto fail; - } +static int mdp5_bind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *ddev = dev_get_drvdata(master); + struct platform_device *pdev = to_platform_device(dev); - ret = mmu->funcs->attach(mmu, iommu_ports, - ARRAY_SIZE(iommu_ports)); - if (ret) { - dev_err(dev->dev, "failed to attach iommu: %d\n", ret); - mmu->funcs->destroy(mmu); - goto fail; - } - } else { - dev_info(dev->dev, "no iommu, fallback to phys " - "contig buffers for scanout\n"); - mmu = NULL; - } - mdp5_kms->mmu = mmu; + DBG(""); - mdp5_kms->id = msm_register_mmu(dev, mmu); - if (mdp5_kms->id < 0) { - ret = mdp5_kms->id; - dev_err(dev->dev, "failed to register mdp5 iommu: %d\n", ret); - goto fail; - } + return mdp5_init(pdev, ddev); +} - ret = modeset_init(mdp5_kms); - if (ret) { - dev_err(dev->dev, "modeset_init failed: %d\n", ret); - goto fail; - } +static void mdp5_unbind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); - dev->mode_config.min_width = 0; - dev->mode_config.min_height = 0; - dev->mode_config.max_width = config->hw->lm.max_width; - dev->mode_config.max_height = config->hw->lm.max_height; + mdp5_destroy(pdev); +} - dev->driver->get_vblank_timestamp = mdp5_get_vblank_timestamp; - dev->driver->get_scanout_position = mdp5_get_scanoutpos; - dev->driver->get_vblank_counter = mdp5_get_vblank_counter; - dev->max_vblank_count = 0xffffffff; - dev->vblank_disable_immediate = true; +static const struct component_ops mdp5_ops = { + .bind = mdp5_bind, + .unbind = mdp5_unbind, +}; - return kms; +static int mdp5_dev_probe(struct platform_device *pdev) +{ + DBG(""); + return component_add(&pdev->dev, &mdp5_ops); +} -fail: - if (kms) - mdp5_destroy(kms); - return ERR_PTR(ret); +static int mdp5_dev_remove(struct platform_device *pdev) +{ + DBG(""); + component_del(&pdev->dev, &mdp5_ops); + return 0; +} + +static const struct of_device_id mdp5_dt_match[] = { + { .compatible = "qcom,mdp5", }, + /* to support downstream DT files */ + { .compatible = "qcom,mdss_mdp", }, + {} +}; +MODULE_DEVICE_TABLE(of, mdp5_dt_match); + +static struct platform_driver mdp5_driver = { + .probe = mdp5_dev_probe, + .remove = mdp5_dev_remove, + .driver = { + .name = "msm_mdp", + .of_match_table = mdp5_dt_match, + }, +}; + +void __init msm_mdp_register(void) +{ + DBG(""); + platform_driver_register(&mdp5_driver); +} + +void __exit msm_mdp_unregister(void) +{ + DBG(""); + platform_driver_unregister(&mdp5_driver); } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h index 9a25898239d3..03738927be10 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h @@ -31,6 +31,8 @@ struct mdp5_kms { struct drm_device *dev; + struct platform_device *pdev; + struct mdp5_cfg_handler *cfg; uint32_t caps; /* MDP capabilities (MDP_CAP_XXX bits) */ @@ -43,29 +45,23 @@ struct mdp5_kms { struct mdp5_ctl_manager *ctlm; /* io/register spaces: */ - void __iomem *mmio, *vbif; - - struct regulator *vdd; + void __iomem *mmio; struct clk *axi_clk; struct clk *ahb_clk; - struct clk *src_clk; struct clk *core_clk; struct clk *lut_clk; struct clk *vsync_clk; /* * lock to protect access to global resources: ie., following register: - * - REG_MDP5_MDP_DISP_INTF_SEL + * - REG_MDP5_DISP_INTF_SEL */ spinlock_t resource_lock; - struct mdp_irq error_handler; + bool rpm_enabled; - struct { - volatile unsigned long enabled_mask; - struct irq_domain *domain; - } irqcontroller; + struct mdp_irq error_handler; }; #define to_mdp5_kms(x) container_of(x, struct mdp5_kms, base) diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c new file mode 100644 index 000000000000..d444a6901fff --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_mdss.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/irqdomain.h> +#include <linux/irq.h> + +#include "msm_drv.h" +#include "mdp5_kms.h" + +/* + * If needed, this can become more specific: something like struct mdp5_mdss, + * which contains a 'struct msm_mdss base' member. + */ +struct msm_mdss { + struct drm_device *dev; + + void __iomem *mmio, *vbif; + + struct regulator *vdd; + + struct { + volatile unsigned long enabled_mask; + struct irq_domain *domain; + } irqcontroller; +}; + +static inline void mdss_write(struct msm_mdss *mdss, u32 reg, u32 data) +{ + msm_writel(data, mdss->mmio + reg); +} + +static inline u32 mdss_read(struct msm_mdss *mdss, u32 reg) +{ + return msm_readl(mdss->mmio + reg); +} + +static irqreturn_t mdss_irq(int irq, void *arg) +{ + struct msm_mdss *mdss = arg; + u32 intr; + + intr = mdss_read(mdss, REG_MDSS_HW_INTR_STATUS); + + VERB("intr=%08x", intr); + + while (intr) { + irq_hw_number_t hwirq = fls(intr) - 1; + + generic_handle_irq(irq_find_mapping( + mdss->irqcontroller.domain, hwirq)); + intr &= ~(1 << hwirq); + } + + return IRQ_HANDLED; +} + +/* + * interrupt-controller implementation, so sub-blocks (MDP/HDMI/eDP/DSI/etc) + * can register to get their irq's delivered + */ + +#define VALID_IRQS (MDSS_HW_INTR_STATUS_INTR_MDP | \ + MDSS_HW_INTR_STATUS_INTR_DSI0 | \ + MDSS_HW_INTR_STATUS_INTR_DSI1 | \ + MDSS_HW_INTR_STATUS_INTR_HDMI | \ + MDSS_HW_INTR_STATUS_INTR_EDP) + +static void mdss_hw_mask_irq(struct irq_data *irqd) +{ + struct msm_mdss *mdss = irq_data_get_irq_chip_data(irqd); + + smp_mb__before_atomic(); + clear_bit(irqd->hwirq, &mdss->irqcontroller.enabled_mask); + smp_mb__after_atomic(); +} + +static void mdss_hw_unmask_irq(struct irq_data *irqd) +{ + struct msm_mdss *mdss = irq_data_get_irq_chip_data(irqd); + + smp_mb__before_atomic(); + set_bit(irqd->hwirq, &mdss->irqcontroller.enabled_mask); + smp_mb__after_atomic(); +} + +static struct irq_chip mdss_hw_irq_chip = { + .name = "mdss", + .irq_mask = mdss_hw_mask_irq, + .irq_unmask = mdss_hw_unmask_irq, +}; + +static int mdss_hw_irqdomain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct msm_mdss *mdss = d->host_data; + + if (!(VALID_IRQS & (1 << hwirq))) + return -EPERM; + + irq_set_chip_and_handler(irq, &mdss_hw_irq_chip, handle_level_irq); + irq_set_chip_data(irq, mdss); + + return 0; +} + +static struct irq_domain_ops mdss_hw_irqdomain_ops = { + .map = mdss_hw_irqdomain_map, + .xlate = irq_domain_xlate_onecell, +}; + + +static int mdss_irq_domain_init(struct msm_mdss *mdss) +{ + struct device *dev = mdss->dev->dev; + struct irq_domain *d; + + d = irq_domain_add_linear(dev->of_node, 32, &mdss_hw_irqdomain_ops, + mdss); + if (!d) { + dev_err(dev, "mdss irq domain add failed\n"); + return -ENXIO; + } + + mdss->irqcontroller.enabled_mask = 0; + mdss->irqcontroller.domain = d; + + return 0; +} + +void msm_mdss_destroy(struct drm_device *dev) +{ + struct msm_drm_private *priv = dev->dev_private; + struct msm_mdss *mdss = priv->mdss; + + if (!mdss) + return; + + irq_domain_remove(mdss->irqcontroller.domain); + mdss->irqcontroller.domain = NULL; + + regulator_disable(mdss->vdd); + + pm_runtime_put_sync(dev->dev); + + pm_runtime_disable(dev->dev); +} + +int msm_mdss_init(struct drm_device *dev) +{ + struct platform_device *pdev = dev->platformdev; + struct msm_drm_private *priv = dev->dev_private; + struct msm_mdss *mdss; + int ret; + + DBG(""); + + if (!of_device_is_compatible(dev->dev->of_node, "qcom,mdss")) + return 0; + + mdss = devm_kzalloc(dev->dev, sizeof(*mdss), GFP_KERNEL); + if (!mdss) { + ret = -ENOMEM; + goto fail; + } + + mdss->dev = dev; + + mdss->mmio = msm_ioremap(pdev, "mdss_phys", "MDSS"); + if (IS_ERR(mdss->mmio)) { + ret = PTR_ERR(mdss->mmio); + goto fail; + } + + mdss->vbif = msm_ioremap(pdev, "vbif_phys", "VBIF"); + if (IS_ERR(mdss->vbif)) { + ret = PTR_ERR(mdss->vbif); + goto fail; + } + + /* Regulator to enable GDSCs in downstream kernels */ + mdss->vdd = devm_regulator_get(dev->dev, "vdd"); + if (IS_ERR(mdss->vdd)) { + ret = PTR_ERR(mdss->vdd); + goto fail; + } + + ret = regulator_enable(mdss->vdd); + if (ret) { + dev_err(dev->dev, "failed to enable regulator vdd: %d\n", + ret); + goto fail; + } + + ret = devm_request_irq(dev->dev, platform_get_irq(pdev, 0), + mdss_irq, 0, "mdss_isr", mdss); + if (ret) { + dev_err(dev->dev, "failed to init irq: %d\n", ret); + goto fail_irq; + } + + ret = mdss_irq_domain_init(mdss); + if (ret) { + dev_err(dev->dev, "failed to init sub-block irqs: %d\n", ret); + goto fail_irq; + } + + priv->mdss = mdss; + + pm_runtime_enable(dev->dev); + + /* + * TODO: This is needed as the MDSS GDSC is only tied to MDSS's power + * domain. Remove this once runtime PM is adapted for all the devices. + */ + pm_runtime_get_sync(dev->dev); + + return 0; +fail_irq: + regulator_disable(mdss->vdd); +fail: + return ret; +} diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c index 6f425c25d9fe..27d7b55b52c9 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c @@ -42,7 +42,7 @@ * * configured: * The block is allocated to some client, and assigned to that - * client in MDP5_MDP_SMP_ALLOC registers. + * client in MDP5_SMP_ALLOC registers. * * inuse: * The block is being actively used by a client. @@ -59,7 +59,7 @@ * mdp5_smp_commit. * * 2) mdp5_smp_configure(): - * As hw is programmed, before FLUSH, MDP5_MDP_SMP_ALLOC registers + * As hw is programmed, before FLUSH, MDP5_SMP_ALLOC registers * are configured for the union(pending, inuse) * Current pending is copied to configured. * It is assumed that mdp5_smp_request and mdp5_smp_configure not run @@ -311,25 +311,25 @@ static void update_smp_state(struct mdp5_smp *smp, int idx = blk / 3; int fld = blk % 3; - val = mdp5_read(mdp5_kms, REG_MDP5_MDP_SMP_ALLOC_W_REG(0, idx)); + val = mdp5_read(mdp5_kms, REG_MDP5_SMP_ALLOC_W_REG(idx)); switch (fld) { case 0: - val &= ~MDP5_MDP_SMP_ALLOC_W_REG_CLIENT0__MASK; - val |= MDP5_MDP_SMP_ALLOC_W_REG_CLIENT0(cid); + val &= ~MDP5_SMP_ALLOC_W_REG_CLIENT0__MASK; + val |= MDP5_SMP_ALLOC_W_REG_CLIENT0(cid); break; case 1: - val &= ~MDP5_MDP_SMP_ALLOC_W_REG_CLIENT1__MASK; - val |= MDP5_MDP_SMP_ALLOC_W_REG_CLIENT1(cid); + val &= ~MDP5_SMP_ALLOC_W_REG_CLIENT1__MASK; + val |= MDP5_SMP_ALLOC_W_REG_CLIENT1(cid); break; case 2: - val &= ~MDP5_MDP_SMP_ALLOC_W_REG_CLIENT2__MASK; - val |= MDP5_MDP_SMP_ALLOC_W_REG_CLIENT2(cid); + val &= ~MDP5_SMP_ALLOC_W_REG_CLIENT2__MASK; + val |= MDP5_SMP_ALLOC_W_REG_CLIENT2(cid); break; } - mdp5_write(mdp5_kms, REG_MDP5_MDP_SMP_ALLOC_W_REG(0, idx), val); - mdp5_write(mdp5_kms, REG_MDP5_MDP_SMP_ALLOC_R_REG(0, idx), val); + mdp5_write(mdp5_kms, REG_MDP5_SMP_ALLOC_W_REG(idx), val); + mdp5_write(mdp5_kms, REG_MDP5_SMP_ALLOC_R_REG(idx), val); } } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index a02dc2b27739..26f859ec24b3 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -21,6 +21,16 @@ #include "msm_gpu.h" #include "msm_kms.h" + +/* + * MSM driver version: + * - 1.0.0 - initial interface + * - 1.1.0 - adds madvise, and support for submits with > 4 cmd buffers + */ +#define MSM_VERSION_MAJOR 1 +#define MSM_VERSION_MINOR 1 +#define MSM_VERSION_PATCHLEVEL 0 + static void msm_fb_output_poll_changed(struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; @@ -195,6 +205,8 @@ static int msm_drm_uninit(struct device *dev) kfree(vbl_ev); } + msm_gem_shrinker_cleanup(ddev); + drm_kms_helper_poll_fini(ddev); drm_dev_unregister(ddev); @@ -215,10 +227,8 @@ static int msm_drm_uninit(struct device *dev) flush_workqueue(priv->atomic_wq); destroy_workqueue(priv->atomic_wq); - if (kms) { - pm_runtime_disable(dev); + if (kms) kms->funcs->destroy(kms); - } if (gpu) { mutex_lock(&ddev->struct_mutex); @@ -237,6 +247,8 @@ static int msm_drm_uninit(struct device *dev) component_unbind_all(dev, ddev); + msm_mdss_destroy(ddev); + ddev->dev_private = NULL; drm_dev_unref(ddev); @@ -282,6 +294,7 @@ static int msm_init_vram(struct drm_device *dev) if (node) { struct resource r; ret = of_address_to_resource(node, 0, &r); + of_node_put(node); if (ret) return ret; size = r.end - r.start; @@ -350,6 +363,14 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) } ddev->dev_private = priv; + priv->dev = ddev; + + ret = msm_mdss_init(ddev); + if (ret) { + kfree(priv); + drm_dev_unref(ddev); + return ret; + } priv->wq = alloc_ordered_workqueue("msm", 0); priv->atomic_wq = alloc_ordered_workqueue("msm:atomic", 0); @@ -365,6 +386,7 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) /* Bind all our sub-components: */ ret = component_bind_all(dev, ddev); if (ret) { + msm_mdss_destroy(ddev); kfree(priv); drm_dev_unref(ddev); return ret; @@ -374,9 +396,12 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) if (ret) goto fail; + msm_gem_shrinker_init(ddev); + switch (get_mdp_ver(pdev)) { case 4: kms = mdp4_kms_init(ddev); + priv->kms = kms; break; case 5: kms = mdp5_kms_init(ddev); @@ -398,10 +423,7 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) goto fail; } - priv->kms = kms; - if (kms) { - pm_runtime_enable(dev); ret = kms->funcs->hw_init(kms); if (ret) { dev_err(dev, "kms hw init failed: %d\n", ret); @@ -417,12 +439,14 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) goto fail; } - pm_runtime_get_sync(dev); - ret = drm_irq_install(ddev, platform_get_irq(pdev, 0)); - pm_runtime_put_sync(dev); - if (ret < 0) { - dev_err(dev, "failed to install IRQ handler\n"); - goto fail; + if (kms) { + pm_runtime_get_sync(dev); + ret = drm_irq_install(ddev, kms->irq); + pm_runtime_put_sync(dev); + if (ret < 0) { + dev_err(dev, "failed to install IRQ handler\n"); + goto fail; + } } ret = drm_dev_register(ddev, 0); @@ -682,6 +706,44 @@ static int msm_ioctl_wait_fence(struct drm_device *dev, void *data, return msm_wait_fence(priv->gpu->fctx, args->fence, &timeout, true); } +static int msm_ioctl_gem_madvise(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_gem_madvise *args = data; + struct drm_gem_object *obj; + int ret; + + switch (args->madv) { + case MSM_MADV_DONTNEED: + case MSM_MADV_WILLNEED: + break; + default: + return -EINVAL; + } + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + obj = drm_gem_object_lookup(file, args->handle); + if (!obj) { + ret = -ENOENT; + goto unlock; + } + + ret = msm_gem_madvise(obj, args->madv); + if (ret >= 0) { + args->retained = ret; + ret = 0; + } + + drm_gem_object_unreference(obj); + +unlock: + mutex_unlock(&dev->struct_mutex); + return ret; +} + static const struct drm_ioctl_desc msm_ioctls[] = { DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_AUTH|DRM_RENDER_ALLOW), @@ -690,6 +752,7 @@ static const struct drm_ioctl_desc msm_ioctls[] = { DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT, msm_ioctl_gem_submit, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE, msm_ioctl_wait_fence, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_GEM_MADVISE, msm_ioctl_gem_madvise, DRM_AUTH|DRM_RENDER_ALLOW), }; static const struct vm_operations_struct vm_ops = { @@ -755,8 +818,9 @@ static struct drm_driver msm_driver = { .name = "msm", .desc = "MSM Snapdragon DRM", .date = "20130625", - .major = 1, - .minor = 0, + .major = MSM_VERSION_MAJOR, + .minor = MSM_VERSION_MINOR, + .patchlevel = MSM_VERSION_PATCHLEVEL, }; #ifdef CONFIG_PM_SLEEP @@ -796,22 +860,146 @@ static int compare_of(struct device *dev, void *data) return dev->of_node == data; } -static int add_components(struct device *dev, struct component_match **matchptr, - const char *name) +/* + * Identify what components need to be added by parsing what remote-endpoints + * our MDP output ports are connected to. In the case of LVDS on MDP4, there + * is no external component that we need to add since LVDS is within MDP4 + * itself. + */ +static int add_components_mdp(struct device *mdp_dev, + struct component_match **matchptr) +{ + struct device_node *np = mdp_dev->of_node; + struct device_node *ep_node; + struct device *master_dev; + + /* + * on MDP4 based platforms, the MDP platform device is the component + * master that adds other display interface components to itself. + * + * on MDP5 based platforms, the MDSS platform device is the component + * master that adds MDP5 and other display interface components to + * itself. + */ + if (of_device_is_compatible(np, "qcom,mdp4")) + master_dev = mdp_dev; + else + master_dev = mdp_dev->parent; + + for_each_endpoint_of_node(np, ep_node) { + struct device_node *intf; + struct of_endpoint ep; + int ret; + + ret = of_graph_parse_endpoint(ep_node, &ep); + if (ret) { + dev_err(mdp_dev, "unable to parse port endpoint\n"); + of_node_put(ep_node); + return ret; + } + + /* + * The LCDC/LVDS port on MDP4 is a speacial case where the + * remote-endpoint isn't a component that we need to add + */ + if (of_device_is_compatible(np, "qcom,mdp4") && + ep.port == 0) { + of_node_put(ep_node); + continue; + } + + /* + * It's okay if some of the ports don't have a remote endpoint + * specified. It just means that the port isn't connected to + * any external interface. + */ + intf = of_graph_get_remote_port_parent(ep_node); + if (!intf) { + of_node_put(ep_node); + continue; + } + + component_match_add(master_dev, matchptr, compare_of, intf); + + of_node_put(intf); + of_node_put(ep_node); + } + + return 0; +} + +static int compare_name_mdp(struct device *dev, void *data) { - struct device_node *np = dev->of_node; - unsigned i; + return (strstr(dev_name(dev), "mdp") != NULL); +} + +static int add_display_components(struct device *dev, + struct component_match **matchptr) +{ + struct device *mdp_dev; + int ret; + + /* + * MDP5 based devices don't have a flat hierarchy. There is a top level + * parent: MDSS, and children: MDP5, DSI, HDMI, eDP etc. Populate the + * children devices, find the MDP5 node, and then add the interfaces + * to our components list. + */ + if (of_device_is_compatible(dev->of_node, "qcom,mdss")) { + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to populate children devices\n"); + return ret; + } - for (i = 0; ; i++) { - struct device_node *node; + mdp_dev = device_find_child(dev, NULL, compare_name_mdp); + if (!mdp_dev) { + dev_err(dev, "failed to find MDSS MDP node\n"); + of_platform_depopulate(dev); + return -ENODEV; + } - node = of_parse_phandle(np, name, i); - if (!node) - break; + put_device(mdp_dev); - component_match_add(dev, matchptr, compare_of, node); + /* add the MDP component itself */ + component_match_add(dev, matchptr, compare_of, + mdp_dev->of_node); + } else { + /* MDP4 */ + mdp_dev = dev; } + ret = add_components_mdp(mdp_dev, matchptr); + if (ret) + of_platform_depopulate(dev); + + return ret; +} + +/* + * We don't know what's the best binding to link the gpu with the drm device. + * Fow now, we just hunt for all the possible gpus that we support, and add them + * as components. + */ +static const struct of_device_id msm_gpu_match[] = { + { .compatible = "qcom,adreno-3xx" }, + { .compatible = "qcom,kgsl-3d0" }, + { }, +}; + +static int add_gpu_components(struct device *dev, + struct component_match **matchptr) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, msm_gpu_match); + if (!np) + return 0; + + component_match_add(dev, matchptr, compare_of, np); + + of_node_put(np); + return 0; } @@ -837,9 +1025,15 @@ static const struct component_master_ops msm_drm_ops = { static int msm_pdev_probe(struct platform_device *pdev) { struct component_match *match = NULL; + int ret; - add_components(&pdev->dev, &match, "connectors"); - add_components(&pdev->dev, &match, "gpus"); + ret = add_display_components(&pdev->dev, &match); + if (ret) + return ret; + + ret = add_gpu_components(&pdev->dev, &match); + if (ret) + return ret; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); return component_master_add_with_match(&pdev->dev, &msm_drm_ops, match); @@ -848,20 +1042,14 @@ static int msm_pdev_probe(struct platform_device *pdev) static int msm_pdev_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &msm_drm_ops); + of_platform_depopulate(&pdev->dev); return 0; } -static const struct platform_device_id msm_id[] = { - { "mdp", 0 }, - { } -}; - static const struct of_device_id dt_match[] = { - { .compatible = "qcom,mdp4", .data = (void *) 4 }, /* mdp4 */ - { .compatible = "qcom,mdp5", .data = (void *) 5 }, /* mdp5 */ - /* to support downstream DT files */ - { .compatible = "qcom,mdss_mdp", .data = (void *) 5 }, /* mdp5 */ + { .compatible = "qcom,mdp4", .data = (void *)4 }, /* MDP4 */ + { .compatible = "qcom,mdss", .data = (void *)5 }, /* MDP5 MDSS */ {} }; MODULE_DEVICE_TABLE(of, dt_match); @@ -874,12 +1062,12 @@ static struct platform_driver msm_platform_driver = { .of_match_table = dt_match, .pm = &msm_pm_ops, }, - .id_table = msm_id, }; static int __init msm_drm_register(void) { DBG("init"); + msm_mdp_register(); msm_dsi_register(); msm_edp_register(); msm_hdmi_register(); @@ -895,6 +1083,7 @@ static void __exit msm_drm_unregister(void) adreno_unregister(); msm_edp_unregister(); msm_dsi_unregister(); + msm_mdp_unregister(); } module_init(msm_drm_register); diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 5b2963f32291..b4bc7f1ef717 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -46,6 +46,7 @@ struct msm_kms; struct msm_gpu; struct msm_mmu; +struct msm_mdss; struct msm_rd_state; struct msm_perf_state; struct msm_gem_submit; @@ -77,11 +78,16 @@ struct msm_vblank_ctrl { struct msm_drm_private { + struct drm_device *dev; + struct msm_kms *kms; /* subordinate devices, if present: */ struct platform_device *gpu_pdev; + /* top level MDSS wrapper device (for MDP5 only) */ + struct msm_mdss *mdss; + /* possibly this should be in the kms component, but it is * shared by both mdp4 and mdp5.. */ @@ -147,6 +153,9 @@ struct msm_drm_private { struct drm_mm mm; } vram; + struct notifier_block vmap_notifier; + struct shrinker shrinker; + struct msm_vblank_ctrl vblank_ctrl; }; @@ -165,6 +174,9 @@ void msm_gem_submit_free(struct msm_gem_submit *submit); int msm_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_file *file); +void msm_gem_shrinker_init(struct drm_device *dev); +void msm_gem_shrinker_cleanup(struct drm_device *dev); + int msm_gem_mmap_obj(struct drm_gem_object *obj, struct vm_area_struct *vma); int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma); @@ -189,8 +201,13 @@ struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sg); int msm_gem_prime_pin(struct drm_gem_object *obj); void msm_gem_prime_unpin(struct drm_gem_object *obj); -void *msm_gem_vaddr_locked(struct drm_gem_object *obj); -void *msm_gem_vaddr(struct drm_gem_object *obj); +void *msm_gem_get_vaddr_locked(struct drm_gem_object *obj); +void *msm_gem_get_vaddr(struct drm_gem_object *obj); +void msm_gem_put_vaddr_locked(struct drm_gem_object *obj); +void msm_gem_put_vaddr(struct drm_gem_object *obj); +int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv); +void msm_gem_purge(struct drm_gem_object *obj); +void msm_gem_vunmap(struct drm_gem_object *obj); int msm_gem_sync_object(struct drm_gem_object *obj, struct msm_fence_context *fctx, bool exclusive); void msm_gem_move_to_active(struct drm_gem_object *obj, @@ -257,6 +274,9 @@ static inline int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, } #endif +void __init msm_mdp_register(void); +void __exit msm_mdp_unregister(void); + #ifdef CONFIG_DEBUG_FS void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m); void msm_gem_describe_objects(struct list_head *list, struct seq_file *m); diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index 7919c24c6ddd..95cf8fe72ee5 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -49,8 +49,8 @@ static void msm_framebuffer_destroy(struct drm_framebuffer *fb) for (i = 0; i < n; i++) { struct drm_gem_object *bo = msm_fb->planes[i]; - if (bo) - drm_gem_object_unreference_unlocked(bo); + + drm_gem_object_unreference_unlocked(bo); } kfree(msm_fb); diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index a9223bea871b..ffd4a338ca12 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -158,7 +158,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, dev->mode_config.fb_base = paddr; - fbi->screen_base = msm_gem_vaddr_locked(fbdev->bo); + fbi->screen_base = msm_gem_get_vaddr_locked(fbdev->bo); if (IS_ERR(fbi->screen_base)) { ret = PTR_ERR(fbi->screen_base); goto fail_unlock; @@ -251,6 +251,7 @@ void msm_fbdev_free(struct drm_device *dev) /* this will free the backing object */ if (fbdev->fb) { + msm_gem_put_vaddr(fbdev->bo); drm_framebuffer_unregister_private(fbdev->fb); drm_framebuffer_remove(fbdev->fb); } diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 69836f5685b1..6cd4af443139 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -276,6 +276,26 @@ uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj) return offset; } +static void +put_iova(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct msm_drm_private *priv = obj->dev->dev_private; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + int id; + + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + + for (id = 0; id < ARRAY_SIZE(msm_obj->domain); id++) { + struct msm_mmu *mmu = priv->mmus[id]; + if (mmu && msm_obj->domain[id].iova) { + uint32_t offset = msm_obj->domain[id].iova; + mmu->funcs->unmap(mmu, offset, msm_obj->sgt, obj->size); + msm_obj->domain[id].iova = 0; + } + } +} + /* should be called under struct_mutex.. although it can be called * from atomic context without struct_mutex to acquire an extra * iova ref if you know one is already held. @@ -388,7 +408,7 @@ fail: return ret; } -void *msm_gem_vaddr_locked(struct drm_gem_object *obj) +void *msm_gem_get_vaddr_locked(struct drm_gem_object *obj) { struct msm_gem_object *msm_obj = to_msm_bo(obj); WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex)); @@ -401,18 +421,91 @@ void *msm_gem_vaddr_locked(struct drm_gem_object *obj) if (msm_obj->vaddr == NULL) return ERR_PTR(-ENOMEM); } + msm_obj->vmap_count++; return msm_obj->vaddr; } -void *msm_gem_vaddr(struct drm_gem_object *obj) +void *msm_gem_get_vaddr(struct drm_gem_object *obj) { void *ret; mutex_lock(&obj->dev->struct_mutex); - ret = msm_gem_vaddr_locked(obj); + ret = msm_gem_get_vaddr_locked(obj); mutex_unlock(&obj->dev->struct_mutex); return ret; } +void msm_gem_put_vaddr_locked(struct drm_gem_object *obj) +{ + struct msm_gem_object *msm_obj = to_msm_bo(obj); + WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex)); + WARN_ON(msm_obj->vmap_count < 1); + msm_obj->vmap_count--; +} + +void msm_gem_put_vaddr(struct drm_gem_object *obj) +{ + mutex_lock(&obj->dev->struct_mutex); + msm_gem_put_vaddr_locked(obj); + mutex_unlock(&obj->dev->struct_mutex); +} + +/* Update madvise status, returns true if not purged, else + * false or -errno. + */ +int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv) +{ + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex)); + + if (msm_obj->madv != __MSM_MADV_PURGED) + msm_obj->madv = madv; + + return (msm_obj->madv != __MSM_MADV_PURGED); +} + +void msm_gem_purge(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + WARN_ON(!is_purgeable(msm_obj)); + WARN_ON(obj->import_attach); + + put_iova(obj); + + msm_gem_vunmap(obj); + + put_pages(obj); + + msm_obj->madv = __MSM_MADV_PURGED; + + drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping); + drm_gem_free_mmap_offset(obj); + + /* Our goal here is to return as much of the memory as + * is possible back to the system as we are called from OOM. + * To do this we must instruct the shmfs to drop all of its + * backing pages, *now*. + */ + shmem_truncate_range(file_inode(obj->filp), 0, (loff_t)-1); + + invalidate_mapping_pages(file_inode(obj->filp)->i_mapping, + 0, (loff_t)-1); +} + +void msm_gem_vunmap(struct drm_gem_object *obj) +{ + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + if (!msm_obj->vaddr || WARN_ON(!is_vunmapable(msm_obj))) + return; + + vunmap(msm_obj->vaddr); + msm_obj->vaddr = NULL; +} + /* must be called before _move_to_active().. */ int msm_gem_sync_object(struct drm_gem_object *obj, struct msm_fence_context *fctx, bool exclusive) @@ -464,6 +557,7 @@ void msm_gem_move_to_active(struct drm_gem_object *obj, struct msm_gpu *gpu, bool exclusive, struct fence *fence) { struct msm_gem_object *msm_obj = to_msm_bo(obj); + WARN_ON(msm_obj->madv != MSM_MADV_WILLNEED); msm_obj->gpu = gpu; if (exclusive) reservation_object_add_excl_fence(msm_obj->resv, fence); @@ -532,13 +626,27 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m) struct reservation_object_list *fobj; struct fence *fence; uint64_t off = drm_vma_node_start(&obj->vma_node); + const char *madv; WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex)); - seq_printf(m, "%08x: %c %2d (%2d) %08llx %p %zu\n", + switch (msm_obj->madv) { + case __MSM_MADV_PURGED: + madv = " purged"; + break; + case MSM_MADV_DONTNEED: + madv = " purgeable"; + break; + case MSM_MADV_WILLNEED: + default: + madv = ""; + break; + } + + seq_printf(m, "%08x: %c %2d (%2d) %08llx %p %zu%s\n", msm_obj->flags, is_active(msm_obj) ? 'A' : 'I', obj->name, obj->refcount.refcount.counter, - off, msm_obj->vaddr, obj->size); + off, msm_obj->vaddr, obj->size, madv); rcu_read_lock(); fobj = rcu_dereference(robj->fence); @@ -578,9 +686,7 @@ void msm_gem_describe_objects(struct list_head *list, struct seq_file *m) void msm_gem_free_object(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; - struct msm_drm_private *priv = obj->dev->dev_private; struct msm_gem_object *msm_obj = to_msm_bo(obj); - int id; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); @@ -589,13 +695,7 @@ void msm_gem_free_object(struct drm_gem_object *obj) list_del(&msm_obj->mm_list); - for (id = 0; id < ARRAY_SIZE(msm_obj->domain); id++) { - struct msm_mmu *mmu = priv->mmus[id]; - if (mmu && msm_obj->domain[id].iova) { - uint32_t offset = msm_obj->domain[id].iova; - mmu->funcs->unmap(mmu, offset, msm_obj->sgt, obj->size); - } - } + put_iova(obj); if (obj->import_attach) { if (msm_obj->vaddr) @@ -609,7 +709,7 @@ void msm_gem_free_object(struct drm_gem_object *obj) drm_prime_gem_destroy(obj, msm_obj->sgt); } else { - vunmap(msm_obj->vaddr); + msm_gem_vunmap(obj); put_pages(obj); } @@ -688,6 +788,7 @@ static int msm_gem_new_impl(struct drm_device *dev, msm_obj->vram_node = (void *)&msm_obj[1]; msm_obj->flags = flags; + msm_obj->madv = MSM_MADV_WILLNEED; if (resv) { msm_obj->resv = resv; @@ -729,9 +830,7 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, return obj; fail: - if (obj) - drm_gem_object_unreference(obj); - + drm_gem_object_unreference(obj); return ERR_PTR(ret); } @@ -774,8 +873,6 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, return obj; fail: - if (obj) - drm_gem_object_unreference_unlocked(obj); - + drm_gem_object_unreference_unlocked(obj); return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 9facd4b6ffd9..b2f13cfe945e 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -29,6 +29,16 @@ struct msm_gem_object { uint32_t flags; + /** + * Advice: are the backing pages purgeable? + */ + uint8_t madv; + + /** + * count of active vmap'ing + */ + uint8_t vmap_count; + /* And object is either: * inactive - on priv->inactive_list * active - on one one of the gpu's active_list.. well, at @@ -72,7 +82,16 @@ static inline bool is_active(struct msm_gem_object *msm_obj) return msm_obj->gpu != NULL; } -#define MAX_CMDS 4 +static inline bool is_purgeable(struct msm_gem_object *msm_obj) +{ + return (msm_obj->madv == MSM_MADV_DONTNEED) && msm_obj->sgt && + !msm_obj->base.dma_buf && !msm_obj->base.import_attach; +} + +static inline bool is_vunmapable(struct msm_gem_object *msm_obj) +{ + return (msm_obj->vmap_count == 0) && msm_obj->vaddr; +} /* Created per submit-ioctl, to track bo's and cmdstream bufs, etc, * associated with the cmdstream submission for synchronization (and @@ -95,7 +114,7 @@ struct msm_gem_submit { uint32_t size; /* in dwords */ uint32_t iova; uint32_t idx; /* cmdstream buffer idx in bos[] */ - } cmd[MAX_CMDS]; + } *cmd; /* array of size nr_cmds */ struct { uint32_t flags; struct msm_gem_object *obj; diff --git a/drivers/gpu/drm/msm/msm_gem_prime.c b/drivers/gpu/drm/msm/msm_gem_prime.c index 6b90890faffe..60bb290700ce 100644 --- a/drivers/gpu/drm/msm/msm_gem_prime.c +++ b/drivers/gpu/drm/msm/msm_gem_prime.c @@ -33,12 +33,12 @@ struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj) void *msm_gem_prime_vmap(struct drm_gem_object *obj) { - return msm_gem_vaddr(obj); + return msm_gem_get_vaddr(obj); } void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) { - /* TODO msm_gem_vunmap() */ + msm_gem_put_vaddr(obj); } int msm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c new file mode 100644 index 000000000000..283d2841ba58 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2016 Red Hat + * Author: Rob Clark <robdclark@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "msm_drv.h" +#include "msm_gem.h" + +static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task) +{ + if (!mutex_is_locked(mutex)) + return false; + +#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_MUTEXES) + return mutex->owner == task; +#else + /* Since UP may be pre-empted, we cannot assume that we own the lock */ + return false; +#endif +} + +static bool msm_gem_shrinker_lock(struct drm_device *dev, bool *unlock) +{ + if (!mutex_trylock(&dev->struct_mutex)) { + if (!mutex_is_locked_by(&dev->struct_mutex, current)) + return false; + *unlock = false; + } else { + *unlock = true; + } + + return true; +} + + +static unsigned long +msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc) +{ + struct msm_drm_private *priv = + container_of(shrinker, struct msm_drm_private, shrinker); + struct drm_device *dev = priv->dev; + struct msm_gem_object *msm_obj; + unsigned long count = 0; + bool unlock; + + if (!msm_gem_shrinker_lock(dev, &unlock)) + return 0; + + list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) { + if (is_purgeable(msm_obj)) + count += msm_obj->base.size >> PAGE_SHIFT; + } + + if (unlock) + mutex_unlock(&dev->struct_mutex); + + return count; +} + +static unsigned long +msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) +{ + struct msm_drm_private *priv = + container_of(shrinker, struct msm_drm_private, shrinker); + struct drm_device *dev = priv->dev; + struct msm_gem_object *msm_obj; + unsigned long freed = 0; + bool unlock; + + if (!msm_gem_shrinker_lock(dev, &unlock)) + return SHRINK_STOP; + + list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) { + if (freed >= sc->nr_to_scan) + break; + if (is_purgeable(msm_obj)) { + msm_gem_purge(&msm_obj->base); + freed += msm_obj->base.size >> PAGE_SHIFT; + } + } + + if (unlock) + mutex_unlock(&dev->struct_mutex); + + if (freed > 0) + pr_info_ratelimited("Purging %lu bytes\n", freed << PAGE_SHIFT); + + return freed; +} + +static int +msm_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr) +{ + struct msm_drm_private *priv = + container_of(nb, struct msm_drm_private, vmap_notifier); + struct drm_device *dev = priv->dev; + struct msm_gem_object *msm_obj; + unsigned unmapped = 0; + bool unlock; + + if (!msm_gem_shrinker_lock(dev, &unlock)) + return NOTIFY_DONE; + + list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) { + if (is_vunmapable(msm_obj)) { + msm_gem_vunmap(&msm_obj->base); + /* since we don't know any better, lets bail after a few + * and if necessary the shrinker will be invoked again. + * Seems better than unmapping *everything* + */ + if (++unmapped >= 15) + break; + } + } + + if (unlock) + mutex_unlock(&dev->struct_mutex); + + *(unsigned long *)ptr += unmapped; + + if (unmapped > 0) + pr_info_ratelimited("Purging %u vmaps\n", unmapped); + + return NOTIFY_DONE; +} + +/** + * msm_gem_shrinker_init - Initialize msm shrinker + * @dev_priv: msm device + * + * This function registers and sets up the msm shrinker. + */ +void msm_gem_shrinker_init(struct drm_device *dev) +{ + struct msm_drm_private *priv = dev->dev_private; + priv->shrinker.count_objects = msm_gem_shrinker_count; + priv->shrinker.scan_objects = msm_gem_shrinker_scan; + priv->shrinker.seeks = DEFAULT_SEEKS; + WARN_ON(register_shrinker(&priv->shrinker)); + + priv->vmap_notifier.notifier_call = msm_gem_shrinker_vmap; + WARN_ON(register_vmap_purge_notifier(&priv->vmap_notifier)); +} + +/** + * msm_gem_shrinker_cleanup - Clean up msm shrinker + * @dev_priv: msm device + * + * This function unregisters the msm shrinker. + */ +void msm_gem_shrinker_cleanup(struct drm_device *dev) +{ + struct msm_drm_private *priv = dev->dev_private; + WARN_ON(unregister_vmap_purge_notifier(&priv->vmap_notifier)); + unregister_shrinker(&priv->shrinker); +} diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index eb4bb8b2f3a5..9766f9ae4b7d 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -29,10 +29,11 @@ #define BO_PINNED 0x2000 static struct msm_gem_submit *submit_create(struct drm_device *dev, - struct msm_gpu *gpu, int nr) + struct msm_gpu *gpu, int nr_bos, int nr_cmds) { struct msm_gem_submit *submit; - int sz = sizeof(*submit) + (nr * sizeof(submit->bos[0])); + int sz = sizeof(*submit) + (nr_bos * sizeof(submit->bos[0])) + + (nr_cmds * sizeof(*submit->cmd)); submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); if (!submit) @@ -42,6 +43,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, submit->gpu = gpu; submit->fence = NULL; submit->pid = get_pid(task_pid(current)); + submit->cmd = (void *)&submit->bos[nr_bos]; /* initially, until copy_from_user() and bo lookup succeeds: */ submit->nr_bos = 0; @@ -279,7 +281,7 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob /* For now, just map the entire thing. Eventually we probably * to do it page-by-page, w/ kmap() if not vmap()d.. */ - ptr = msm_gem_vaddr_locked(&obj->base); + ptr = msm_gem_get_vaddr_locked(&obj->base); if (IS_ERR(ptr)) { ret = PTR_ERR(ptr); @@ -332,6 +334,8 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob last_offset = off; } + msm_gem_put_vaddr_locked(&obj->base); + return 0; } @@ -369,14 +373,15 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, if (args->pipe != MSM_PIPE_3D0) return -EINVAL; - if (args->nr_cmds > MAX_CMDS) - return -EINVAL; - - submit = submit_create(dev, gpu, args->nr_bos); - if (!submit) - return -ENOMEM; + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; - mutex_lock(&dev->struct_mutex); + submit = submit_create(dev, gpu, args->nr_bos, args->nr_cmds); + if (!submit) { + ret = -ENOMEM; + goto out_unlock; + } ret = submit_lookup_objects(submit, args, file); if (ret) @@ -462,6 +467,7 @@ out: submit_cleanup(submit); if (ret) msm_gem_submit_free(submit); +out_unlock: mutex_unlock(&dev->struct_mutex); return ret; } diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index a7a0b6d9b057..3a294d0da3a0 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -59,10 +59,10 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint32_t iova, return -EINVAL; for_each_sg(sgt->sgl, sg, sgt->nents, i) { - u32 pa = sg_phys(sg) - sg->offset; + dma_addr_t pa = sg_phys(sg) - sg->offset; size_t bytes = sg->length + sg->offset; - VERB("map[%d]: %08x %08x(%zx)", i, iova, pa, bytes); + VERB("map[%d]: %08x %08lx(%zx)", i, da, (unsigned long)pa, bytes); ret = iommu_map(domain, da, pa, bytes, prot); if (ret) @@ -101,7 +101,7 @@ static int msm_iommu_unmap(struct msm_mmu *mmu, uint32_t iova, if (unmapped < bytes) return unmapped; - VERB("unmap[%d]: %08x(%zx)", i, iova, bytes); + VERB("unmap[%d]: %08x(%zx)", i, da, bytes); BUG_ON(!PAGE_ALIGNED(bytes)); diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index e32222c3d44f..40e41e5cdbc6 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -61,10 +61,8 @@ struct msm_kms_funcs { struct msm_kms { const struct msm_kms_funcs *funcs; - /* irq handling: */ - bool in_irq; - struct list_head irq_list; /* list of mdp4_irq */ - uint32_t vblank_mask; /* irq bits set for userspace vblank */ + /* irq number to be passed on to drm_irq_install */ + int irq; }; static inline void msm_kms_init(struct msm_kms *kms, @@ -75,5 +73,7 @@ static inline void msm_kms_init(struct msm_kms *kms, struct msm_kms *mdp4_kms_init(struct drm_device *dev); struct msm_kms *mdp5_kms_init(struct drm_device *dev); +int msm_mdss_init(struct drm_device *dev); +void msm_mdss_destroy(struct drm_device *dev); #endif /* __MSM_KMS_H__ */ diff --git a/drivers/gpu/drm/msm/msm_perf.c b/drivers/gpu/drm/msm/msm_perf.c index 830857c47c86..17fe4e53e0d1 100644 --- a/drivers/gpu/drm/msm/msm_perf.c +++ b/drivers/gpu/drm/msm/msm_perf.c @@ -132,7 +132,7 @@ static ssize_t perf_read(struct file *file, char __user *buf, size_t sz, loff_t *ppos) { struct msm_perf_state *perf = file->private_data; - int n = 0, ret; + int n = 0, ret = 0; mutex_lock(&perf->read_lock); @@ -143,9 +143,10 @@ static ssize_t perf_read(struct file *file, char __user *buf, } n = min((int)sz, perf->buftot - perf->bufpos); - ret = copy_to_user(buf, &perf->buf[perf->bufpos], n); - if (ret) + if (copy_to_user(buf, &perf->buf[perf->bufpos], n)) { + ret = -EFAULT; goto out; + } perf->bufpos += n; *ppos += n; diff --git a/drivers/gpu/drm/msm/msm_rd.c b/drivers/gpu/drm/msm/msm_rd.c index 0857710c2ff2..3a5fdfcd67ae 100644 --- a/drivers/gpu/drm/msm/msm_rd.c +++ b/drivers/gpu/drm/msm/msm_rd.c @@ -27,6 +27,11 @@ * This bypasses drm_debugfs_create_files() mainly because we need to use * our own fops for a bit more control. In particular, we don't want to * do anything if userspace doesn't have the debugfs file open. + * + * The module-param "rd_full", which defaults to false, enables snapshotting + * all (non-written) buffers in the submit, rather than just cmdstream bo's. + * This is useful to capture the contents of (for example) vbo's or textures, + * or shader programs (if not emitted inline in cmdstream). */ #ifdef CONFIG_DEBUG_FS @@ -40,6 +45,10 @@ #include "msm_gpu.h" #include "msm_gem.h" +static bool rd_full = false; +MODULE_PARM_DESC(rd_full, "If true, $debugfs/.../rd will snapshot all buffer contents"); +module_param_named(rd_full, rd_full, bool, 0600); + enum rd_sect_type { RD_NONE, RD_TEST, /* ascii text */ @@ -140,9 +149,10 @@ static ssize_t rd_read(struct file *file, char __user *buf, goto out; n = min_t(int, sz, circ_count_to_end(&rd->fifo)); - ret = copy_to_user(buf, fptr, n); - if (ret) + if (copy_to_user(buf, fptr, n)) { + ret = -EFAULT; goto out; + } fifo->tail = (fifo->tail + n) & (BUF_SZ - 1); *ppos += n; @@ -277,6 +287,31 @@ void msm_rd_debugfs_cleanup(struct drm_minor *minor) kfree(rd); } +static void snapshot_buf(struct msm_rd_state *rd, + struct msm_gem_submit *submit, int idx, + uint32_t iova, uint32_t size) +{ + struct msm_gem_object *obj = submit->bos[idx].obj; + const char *buf; + + buf = msm_gem_get_vaddr_locked(&obj->base); + if (IS_ERR(buf)) + return; + + if (iova) { + buf += iova - submit->bos[idx].iova; + } else { + iova = submit->bos[idx].iova; + size = obj->base.size; + } + + rd_write_section(rd, RD_GPUADDR, + (uint32_t[2]){ iova, size }, 8); + rd_write_section(rd, RD_BUFFER_CONTENTS, buf, size); + + msm_gem_put_vaddr_locked(&obj->base); +} + /* called under struct_mutex */ void msm_rd_dump_submit(struct msm_gem_submit *submit) { @@ -300,27 +335,27 @@ void msm_rd_dump_submit(struct msm_gem_submit *submit) rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4)); - /* could be nice to have an option (module-param?) to snapshot - * all the bo's associated with the submit. Handy to see vtx - * buffers, etc. For now just the cmdstream bo's is enough. - */ + if (rd_full) { + for (i = 0; i < submit->nr_bos; i++) { + /* buffers that are written to probably don't start out + * with anything interesting: + */ + if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE) + continue; + + snapshot_buf(rd, submit, i, 0, 0); + } + } for (i = 0; i < submit->nr_cmds; i++) { - uint32_t idx = submit->cmd[i].idx; uint32_t iova = submit->cmd[i].iova; uint32_t szd = submit->cmd[i].size; /* in dwords */ - struct msm_gem_object *obj = submit->bos[idx].obj; - const char *buf = msm_gem_vaddr_locked(&obj->base); - - if (IS_ERR(buf)) - continue; - buf += iova - submit->bos[idx].iova; - - rd_write_section(rd, RD_GPUADDR, - (uint32_t[2]){ iova, szd * 4 }, 8); - rd_write_section(rd, RD_BUFFER_CONTENTS, - buf, szd * 4); + /* snapshot cmdstream bo's (if we haven't already): */ + if (!rd_full) { + snapshot_buf(rd, submit, submit->cmd[i].idx, + submit->cmd[i].iova, szd * 4); + } switch (submit->cmd[i].type) { case MSM_SUBMIT_CMD_IB_TARGET_BUF: diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c index 42f5359cf988..f326cf6a32e6 100644 --- a/drivers/gpu/drm/msm/msm_ringbuffer.c +++ b/drivers/gpu/drm/msm/msm_ringbuffer.c @@ -39,7 +39,7 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size) goto fail; } - ring->start = msm_gem_vaddr_locked(ring->bo); + ring->start = msm_gem_get_vaddr_locked(ring->bo); if (IS_ERR(ring->start)) { ret = PTR_ERR(ring->start); goto fail; @@ -59,7 +59,9 @@ fail: void msm_ringbuffer_destroy(struct msm_ringbuffer *ring) { - if (ring->bo) + if (ring->bo) { + msm_gem_put_vaddr(ring->bo); drm_gem_object_unreference_unlocked(ring->bo); + } kfree(ring); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c index 22706c0a54b5..49bd5da194e1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c @@ -40,7 +40,8 @@ static int gf119_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) { struct nvkm_device *device = outp->base.disp->engine.subdev.device; - nvkm_mask(device, 0x61c110, 0x0f0f0f0f, 0x01010101 * pattern); + const u32 soff = gf119_sor_soff(outp); + nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern); return 0; } diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile index 827711e28226..d3b44651061a 100644 --- a/drivers/gpu/drm/rcar-du/Makefile +++ b/drivers/gpu/drm/rcar-du/Makefile @@ -7,8 +7,8 @@ rcar-du-drm-y := rcar_du_crtc.o \ rcar_du_plane.o \ rcar_du_vgacon.o -rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI) += rcar_du_hdmicon.o \ - rcar_du_hdmienc.o +rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI) += rcar_du_hdmienc.o + rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 55149e9ce28e..ab8645c57e2d 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -19,7 +19,6 @@ #include "rcar_du_drv.h" #include "rcar_du_encoder.h" -#include "rcar_du_hdmicon.h" #include "rcar_du_hdmienc.h" #include "rcar_du_kms.h" #include "rcar_du_lvdscon.h" @@ -174,7 +173,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, break; case DRM_MODE_ENCODER_TMDS: - ret = rcar_du_hdmi_connector_init(rcdu, renc); + /* connector managed by the bridge driver */ break; default: diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index a8669c3e0dd5..7fc10a9c34c3 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -15,7 +15,6 @@ #define __RCAR_DU_ENCODER_H__ #include <drm/drm_crtc.h> -#include <drm/drm_encoder_slave.h> struct rcar_du_device; struct rcar_du_hdmienc; @@ -30,16 +29,16 @@ enum rcar_du_encoder_type { }; struct rcar_du_encoder { - struct drm_encoder_slave slave; + struct drm_encoder base; enum rcar_du_output output; struct rcar_du_hdmienc *hdmi; struct rcar_du_lvdsenc *lvds; }; #define to_rcar_encoder(e) \ - container_of(e, struct rcar_du_encoder, slave.base) + container_of(e, struct rcar_du_encoder, base) -#define rcar_encoder_to_drm_encoder(e) (&(e)->slave.base) +#define rcar_encoder_to_drm_encoder(e) (&(e)->base) struct rcar_du_connector { struct drm_connector connector; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c deleted file mode 100644 index 612b4d5ae098..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * R-Car Display Unit HDMI Connector - * - * Copyright (C) 2014 Renesas Electronics Corporation - * - * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <drm/drmP.h> -#include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> -#include <drm/drm_encoder_slave.h> - -#include "rcar_du_drv.h" -#include "rcar_du_encoder.h" -#include "rcar_du_hdmicon.h" -#include "rcar_du_kms.h" - -#define to_slave_funcs(e) (to_rcar_encoder(e)->slave.slave_funcs) - -static int rcar_du_hdmi_connector_get_modes(struct drm_connector *connector) -{ - struct rcar_du_connector *con = to_rcar_connector(connector); - struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(con->encoder); - const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); - - if (sfuncs->get_modes == NULL) - return 0; - - return sfuncs->get_modes(encoder, connector); -} - -static int rcar_du_hdmi_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct rcar_du_connector *con = to_rcar_connector(connector); - struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(con->encoder); - const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); - - if (sfuncs->mode_valid == NULL) - return MODE_OK; - - return sfuncs->mode_valid(encoder, mode); -} - -static const struct drm_connector_helper_funcs connector_helper_funcs = { - .get_modes = rcar_du_hdmi_connector_get_modes, - .mode_valid = rcar_du_hdmi_connector_mode_valid, -}; - -static enum drm_connector_status -rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force) -{ - struct rcar_du_connector *con = to_rcar_connector(connector); - struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(con->encoder); - const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); - - if (sfuncs->detect == NULL) - return connector_status_unknown; - - return sfuncs->detect(encoder, connector); -} - -static const struct drm_connector_funcs connector_funcs = { - .dpms = drm_atomic_helper_connector_dpms, - .reset = drm_atomic_helper_connector_reset, - .detect = rcar_du_hdmi_connector_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_connector_cleanup, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - -int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, - struct rcar_du_encoder *renc) -{ - struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc); - struct rcar_du_connector *rcon; - struct drm_connector *connector; - int ret; - - rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL); - if (rcon == NULL) - return -ENOMEM; - - connector = &rcon->connector; - connector->display_info.width_mm = 0; - connector->display_info.height_mm = 0; - connector->interlace_allowed = true; - connector->polled = DRM_CONNECTOR_POLL_HPD; - - ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); - if (ret < 0) - return ret; - - drm_connector_helper_add(connector, &connector_helper_funcs); - - connector->dpms = DRM_MODE_DPMS_OFF; - drm_object_property_set_value(&connector->base, - rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); - - ret = drm_mode_connector_attach_encoder(connector, encoder); - if (ret < 0) - return ret; - - rcon->encoder = renc; - - return 0; -} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h deleted file mode 100644 index 87daa949227f..000000000000 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * R-Car Display Unit HDMI Connector - * - * Copyright (C) 2014 Renesas Electronics Corporation - * - * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __RCAR_DU_HDMICON_H__ -#define __RCAR_DU_HDMICON_H__ - -struct rcar_du_device; -struct rcar_du_encoder; - -#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI) -int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, - struct rcar_du_encoder *renc); -#else -static inline int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, - struct rcar_du_encoder *renc) -{ - return -ENOSYS; -} -#endif - -#endif /* __RCAR_DU_HDMICON_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c index 461662d231e2..4de3ff0dbebd 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c @@ -16,7 +16,6 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_encoder_slave.h> #include "rcar_du_drv.h" #include "rcar_du_encoder.h" @@ -25,20 +24,14 @@ struct rcar_du_hdmienc { struct rcar_du_encoder *renc; - struct device *dev; bool enabled; }; #define to_rcar_hdmienc(e) (to_rcar_encoder(e)->hdmi) -#define to_slave_funcs(e) (to_rcar_encoder(e)->slave.slave_funcs) static void rcar_du_hdmienc_disable(struct drm_encoder *encoder) { struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); - const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); - - if (sfuncs->dpms) - sfuncs->dpms(encoder, DRM_MODE_DPMS_OFF); if (hdmienc->renc->lvds) rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc, @@ -50,15 +43,11 @@ static void rcar_du_hdmienc_disable(struct drm_encoder *encoder) static void rcar_du_hdmienc_enable(struct drm_encoder *encoder) { struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); - const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); if (hdmienc->renc->lvds) rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc, true); - if (sfuncs->dpms) - sfuncs->dpms(encoder, DRM_MODE_DPMS_ON); - hdmienc->enabled = true; } @@ -67,29 +56,21 @@ static int rcar_du_hdmienc_atomic_check(struct drm_encoder *encoder, struct drm_connector_state *conn_state) { struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); - const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; - const struct drm_display_mode *mode = &crtc_state->mode; if (hdmienc->renc->lvds) rcar_du_lvdsenc_atomic_check(hdmienc->renc->lvds, adjusted_mode); - if (sfuncs->mode_fixup == NULL) - return 0; - - return sfuncs->mode_fixup(encoder, mode, adjusted_mode) ? 0 : -EINVAL; + return 0; } + static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); - const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); - - if (sfuncs->mode_set) - sfuncs->mode_set(encoder, mode, adjusted_mode); rcar_du_crtc_route_output(encoder->crtc, hdmienc->renc->output); } @@ -109,7 +90,6 @@ static void rcar_du_hdmienc_cleanup(struct drm_encoder *encoder) rcar_du_hdmienc_disable(encoder); drm_encoder_cleanup(encoder); - put_device(hdmienc->dev); } static const struct drm_encoder_funcs encoder_funcs = { @@ -120,8 +100,7 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu, struct rcar_du_encoder *renc, struct device_node *np) { struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc); - struct drm_i2c_encoder_driver *driver; - struct i2c_client *i2c_slave; + struct drm_bridge *bridge; struct rcar_du_hdmienc *hdmienc; int ret; @@ -129,44 +108,29 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu, if (hdmienc == NULL) return -ENOMEM; - /* Locate the slave I2C device and driver. */ - i2c_slave = of_find_i2c_device_by_node(np); - if (!i2c_slave || !i2c_get_clientdata(i2c_slave)) { - dev_dbg(rcdu->dev, - "can't get I2C slave for %s, deferring probe\n", - of_node_full_name(np)); + /* Locate drm bridge from the hdmi encoder DT node */ + bridge = of_drm_find_bridge(np); + if (!bridge) return -EPROBE_DEFER; - } - - hdmienc->dev = &i2c_slave->dev; - - if (hdmienc->dev->driver == NULL) { - dev_dbg(rcdu->dev, - "I2C slave %s not probed yet, deferring probe\n", - dev_name(hdmienc->dev)); - ret = -EPROBE_DEFER; - goto error; - } - - /* Initialize the slave encoder. */ - driver = to_drm_i2c_encoder_driver(to_i2c_driver(hdmienc->dev->driver)); - ret = driver->encoder_init(i2c_slave, rcdu->ddev, &renc->slave); - if (ret < 0) - goto error; ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); if (ret < 0) - goto error; + return ret; drm_encoder_helper_add(encoder, &encoder_helper_funcs); renc->hdmi = hdmienc; hdmienc->renc = renc; - return 0; + /* Link drm_bridge to encoder */ + bridge->encoder = encoder; + + ret = drm_bridge_attach(rcdu->ddev, bridge); + if (ret) { + drm_encoder_cleanup(encoder); + return ret; + } -error: - put_device(hdmienc->dev); - return ret; + return 0; } diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 801110f65a63..0665fb915579 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -15,7 +15,6 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> -#include <drm/drm_encoder_slave.h> #include <drm/bridge/dw_hdmi.h> #include "rockchip_drm_drv.h" diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index f0bd1ee8b128..a822d49a255a 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -79,7 +79,7 @@ int rockchip_register_crtc_funcs(struct drm_crtc *crtc, int pipe = drm_crtc_index(crtc); struct rockchip_drm_private *priv = crtc->dev->dev_private; - if (pipe > ROCKCHIP_MAX_CRTC) + if (pipe >= ROCKCHIP_MAX_CRTC) return -EINVAL; priv->crtc_funcs[pipe] = crtc_funcs; @@ -92,7 +92,7 @@ void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc) int pipe = drm_crtc_index(crtc); struct rockchip_drm_private *priv = crtc->dev->dev_private; - if (pipe > ROCKCHIP_MAX_CRTC) + if (pipe >= ROCKCHIP_MAX_CRTC) return; priv->crtc_funcs[pipe] = NULL; @@ -257,7 +257,7 @@ static void rockchip_drm_unbind(struct device *dev) dev_set_drvdata(dev, NULL); } -void rockchip_drm_lastclose(struct drm_device *dev) +static void rockchip_drm_lastclose(struct drm_device *dev) { struct rockchip_drm_private *priv = dev->dev_private; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 6f64e79b12ec..55c52734c52d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -20,6 +20,7 @@ #include <drm/drm_crtc_helper.h> #include "rockchip_drm_drv.h" +#include "rockchip_drm_fb.h" #include "rockchip_drm_gem.h" #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb) @@ -241,7 +242,7 @@ rockchip_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_cleanup_planes(dev, state); } -struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = { +static struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = { .atomic_commit_tail = rockchip_atomic_commit_tail, }; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 6255e5bcd954..91305eb7d312 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -328,9 +328,9 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win, scl_cal_scale2(src_h, dst_h)); if (is_yuv) { VOP_SCL_SET(vop, win, scale_cbcr_x, - scl_cal_scale2(src_w, dst_w)); + scl_cal_scale2(cbcr_src_w, dst_w)); VOP_SCL_SET(vop, win, scale_cbcr_y, - scl_cal_scale2(src_h, dst_h)); + scl_cal_scale2(cbcr_src_h, dst_h)); } return; } @@ -798,7 +798,7 @@ static const struct drm_plane_helper_funcs plane_helper_funcs = { .atomic_disable = vop_plane_atomic_disable, }; -void vop_atomic_plane_reset(struct drm_plane *plane) +static void vop_atomic_plane_reset(struct drm_plane *plane) { struct vop_plane_state *vop_plane_state = to_vop_plane_state(plane->state); @@ -815,7 +815,7 @@ void vop_atomic_plane_reset(struct drm_plane *plane) plane->state->plane = plane; } -struct drm_plane_state * +static struct drm_plane_state * vop_atomic_plane_duplicate_state(struct drm_plane *plane) { struct vop_plane_state *old_vop_plane_state; @@ -1052,6 +1052,17 @@ static void vop_crtc_destroy(struct drm_crtc *crtc) drm_crtc_cleanup(crtc); } +static void vop_crtc_reset(struct drm_crtc *crtc) +{ + if (crtc->state) + __drm_atomic_helper_crtc_destroy_state(crtc->state); + kfree(crtc->state); + + crtc->state = kzalloc(sizeof(struct rockchip_crtc_state), GFP_KERNEL); + if (crtc->state) + crtc->state->crtc = crtc; +} + static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc) { struct rockchip_crtc_state *rockchip_state; @@ -1077,7 +1088,7 @@ static const struct drm_crtc_funcs vop_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, .destroy = vop_crtc_destroy, - .reset = drm_atomic_helper_crtc_reset, + .reset = vop_crtc_reset, .atomic_duplicate_state = vop_crtc_duplicate_state, .atomic_destroy_state = vop_crtc_destroy_state, }; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 3166b46a5893..919992cdc97e 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -190,7 +190,7 @@ static const struct vop_data rk3288_vop = { .win_size = ARRAY_SIZE(rk3288_vop_win_data), }; -static const struct vop_scl_regs rk3066_win_scl = { +static const struct vop_scl_regs rk3036_win_scl = { .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0), .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16), .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0), @@ -198,7 +198,7 @@ static const struct vop_scl_regs rk3066_win_scl = { }; static const struct vop_win_phy rk3036_win0_data = { - .scl = &rk3066_win_scl, + .scl = &rk3036_win_scl, .data_formats = formats_win_full, .nformats = ARRAY_SIZE(formats_win_full), .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0), @@ -210,6 +210,7 @@ static const struct vop_win_phy rk3036_win0_data = { .yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0), .uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0), .yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0), + .uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16), }; static const struct vop_win_phy rk3036_win1_data = { @@ -299,7 +300,7 @@ static int vop_remove(struct platform_device *pdev) return 0; } -struct platform_driver vop_platform_driver = { +static struct platform_driver vop_platform_driver = { .probe = vop_probe, .remove = vop_remove, .driver = { diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c index f628b6d8f23f..4a192210574f 100644 --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c @@ -77,6 +77,14 @@ static void sun4i_crtc_disable(struct drm_crtc *crtc) DRM_DEBUG_DRIVER("Disabling the CRTC\n"); sun4i_tcon_disable(drv->tcon); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + + crtc->state->event = NULL; + } } static void sun4i_crtc_enable(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index b22acc68d3b2..7092daaf6c43 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -92,7 +92,7 @@ static struct drm_driver sun4i_drv_driver = { /* Frame Buffer Operations */ /* VBlank Operations */ - .get_vblank_counter = drm_vblank_count, + .get_vblank_counter = drm_vblank_no_hw_counter, .enable_vblank = sun4i_drv_enable_vblank, .disable_vblank = sun4i_drv_disable_vblank, }; @@ -299,6 +299,7 @@ static int sun4i_drv_probe(struct platform_device *pdev) count += sun4i_drv_add_endpoints(&pdev->dev, &match, pipeline); + of_node_put(pipeline); DRM_DEBUG_DRIVER("Queued %d outputs on pipeline %d\n", count, i); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index b1c1d8f42c8c..4054d804fe06 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1043,9 +1043,9 @@ out_unlock: return ret; } -static bool ttm_bo_mem_compat(struct ttm_placement *placement, - struct ttm_mem_reg *mem, - uint32_t *new_flags) +bool ttm_bo_mem_compat(struct ttm_placement *placement, + struct ttm_mem_reg *mem, + uint32_t *new_flags) { int i; @@ -1077,6 +1077,7 @@ static bool ttm_bo_mem_compat(struct ttm_placement *placement, return false; } +EXPORT_SYMBOL(ttm_bo_mem_compat); int ttm_bo_validate(struct ttm_buffer_object *bo, struct ttm_placement *placement, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c index 9b078a493996..0cd889015dc5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c @@ -49,6 +49,7 @@ int vmw_dmabuf_pin_in_placement(struct vmw_private *dev_priv, { struct ttm_buffer_object *bo = &buf->base; int ret; + uint32_t new_flags; ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible); if (unlikely(ret != 0)) @@ -60,7 +61,12 @@ int vmw_dmabuf_pin_in_placement(struct vmw_private *dev_priv, if (unlikely(ret != 0)) goto err; - ret = ttm_bo_validate(bo, placement, interruptible, false); + if (buf->pin_count > 0) + ret = ttm_bo_mem_compat(placement, &bo->mem, + &new_flags) == true ? 0 : -EINVAL; + else + ret = ttm_bo_validate(bo, placement, interruptible, false); + if (!ret) vmw_bo_pin_reserved(buf, true); @@ -91,6 +97,7 @@ int vmw_dmabuf_pin_in_vram_or_gmr(struct vmw_private *dev_priv, { struct ttm_buffer_object *bo = &buf->base; int ret; + uint32_t new_flags; ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible); if (unlikely(ret != 0)) @@ -102,6 +109,12 @@ int vmw_dmabuf_pin_in_vram_or_gmr(struct vmw_private *dev_priv, if (unlikely(ret != 0)) goto err; + if (buf->pin_count > 0) { + ret = ttm_bo_mem_compat(&vmw_vram_gmr_placement, &bo->mem, + &new_flags) == true ? 0 : -EINVAL; + goto out_unreserve; + } + ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, interruptible, false); if (likely(ret == 0) || ret == -ERESTARTSYS) @@ -161,6 +174,7 @@ int vmw_dmabuf_pin_in_start_of_vram(struct vmw_private *dev_priv, struct ttm_placement placement; struct ttm_place place; int ret = 0; + uint32_t new_flags; place = vmw_vram_placement.placement[0]; place.lpfn = bo->num_pages; @@ -185,10 +199,15 @@ int vmw_dmabuf_pin_in_start_of_vram(struct vmw_private *dev_priv, */ if (bo->mem.mem_type == TTM_PL_VRAM && bo->mem.start < bo->num_pages && - bo->mem.start > 0) + bo->mem.start > 0 && + buf->pin_count == 0) (void) ttm_bo_validate(bo, &vmw_sys_placement, false, false); - ret = ttm_bo_validate(bo, &placement, interruptible, false); + if (buf->pin_count > 0) + ret = ttm_bo_mem_compat(&placement, &bo->mem, + &new_flags) == true ? 0 : -EINVAL; + else + ret = ttm_bo_validate(bo, &placement, interruptible, false); /* For some reason we didn't end up at the start of vram */ WARN_ON(ret == 0 && bo->offset != 0); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 5d5c9515618d..e8ae3dc476d1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -233,6 +233,7 @@ static int vmw_force_iommu; static int vmw_restrict_iommu; static int vmw_force_coherent; static int vmw_restrict_dma_mask; +static int vmw_assume_16bpp; static int vmw_probe(struct pci_dev *, const struct pci_device_id *); static void vmw_master_init(struct vmw_master *); @@ -249,6 +250,8 @@ MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages"); module_param_named(force_coherent, vmw_force_coherent, int, 0600); MODULE_PARM_DESC(restrict_dma_mask, "Restrict DMA mask to 44 bits with IOMMU"); module_param_named(restrict_dma_mask, vmw_restrict_dma_mask, int, 0600); +MODULE_PARM_DESC(assume_16bpp, "Assume 16-bpp when filtering modes"); +module_param_named(assume_16bpp, vmw_assume_16bpp, int, 0600); static void vmw_print_capabilities(uint32_t capabilities) @@ -660,6 +663,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->vram_start = pci_resource_start(dev->pdev, 1); dev_priv->mmio_start = pci_resource_start(dev->pdev, 2); + dev_priv->assume_16bpp = !!vmw_assume_16bpp; + dev_priv->enable_fb = enable_fbdev; vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2); @@ -706,6 +711,13 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) vmw_read(dev_priv, SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB); + /* + * Workaround for low memory 2D VMs to compensate for the + * allocation taken by fbdev + */ + if (!(dev_priv->capabilities & SVGA_CAP_3D)) + mem_size *= 2; + dev_priv->max_mob_pages = mem_size * 1024 / PAGE_SIZE; dev_priv->prim_bb_mem = vmw_read(dev_priv, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 9a90f824814e..74304b03f9d4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -387,6 +387,7 @@ struct vmw_private { spinlock_t hw_lock; spinlock_t cap_lock; bool has_dx; + bool assume_16bpp; /* * VGA registers. diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 679a4cb98ee3..d2d93959b119 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -517,28 +517,6 @@ static int vmw_fb_kms_framebuffer(struct fb_info *info) par->set_fb = &vfb->base; - if (!par->bo_ptr) { - /* - * Pin before mapping. Since we don't know in what placement - * to pin, call into KMS to do it for us. - */ - ret = vfb->pin(vfb); - if (ret) { - DRM_ERROR("Could not pin the fbdev framebuffer.\n"); - return ret; - } - - ret = ttm_bo_kmap(&par->vmw_bo->base, 0, - par->vmw_bo->base.num_pages, &par->map); - if (ret) { - vfb->unpin(vfb); - DRM_ERROR("Could not map the fbdev framebuffer.\n"); - return ret; - } - - par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite); - } - return 0; } @@ -601,6 +579,31 @@ static int vmw_fb_set_par(struct fb_info *info) if (ret) goto out_unlock; + if (!par->bo_ptr) { + struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(set.fb); + + /* + * Pin before mapping. Since we don't know in what placement + * to pin, call into KMS to do it for us. + */ + ret = vfb->pin(vfb); + if (ret) { + DRM_ERROR("Could not pin the fbdev framebuffer.\n"); + goto out_unlock; + } + + ret = ttm_bo_kmap(&par->vmw_bo->base, 0, + par->vmw_bo->base.num_pages, &par->map); + if (ret) { + vfb->unpin(vfb); + DRM_ERROR("Could not map the fbdev framebuffer.\n"); + goto out_unlock; + } + + par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite); + } + + vmw_fb_dirty_mark(par, par->fb_x, par->fb_y, par->set_fb->width, par->set_fb->height); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 8a69d4da40b5..bf28ccc150df 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1555,14 +1555,10 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }; int i; - u32 assumed_bpp = 2; + u32 assumed_bpp = 4; - /* - * If using screen objects, then assume 32-bpp because that's what the - * SVGA device is assuming - */ - if (dev_priv->active_display_unit == vmw_du_screen_object) - assumed_bpp = 4; + if (dev_priv->assume_16bpp) + assumed_bpp = 2; if (dev_priv->active_display_unit == vmw_du_screen_target) { max_width = min(max_width, dev_priv->stdu_max_width); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c index f0374f9b56ca..e57a0bad7a62 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c @@ -300,6 +300,9 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, break; } + if (retries == RETRIES) + return -EINVAL; + *msg_len = reply_len; *msg = reply; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 9ca818fb034c..41932a7c4f79 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -399,8 +399,10 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv, WARN_ON_ONCE(!stdu->defined); - if (!vfb->dmabuf && new_fb->width == mode->hdisplay && - new_fb->height == mode->vdisplay) + new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb); + + if (new_vfbs && new_vfbs->surface->base_size.width == mode->hdisplay && + new_vfbs->surface->base_size.height == mode->vdisplay) new_content_type = SAME_AS_DISPLAY; else if (vfb->dmabuf) new_content_type = SEPARATE_DMA; @@ -444,7 +446,6 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv, content_srf.mip_levels[0] = 1; content_srf.multisample_count = 0; } else { - new_vfbs = vmw_framebuffer_to_vfbs(new_fb); content_srf = *new_vfbs->surface; } @@ -464,7 +465,6 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv, return ret; } } else if (new_content_type == SAME_AS_DISPLAY) { - new_vfbs = vmw_framebuffer_to_vfbs(new_fb); new_display_srf = vmw_surface_reference(new_vfbs->surface); } |