Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/omapdrm: Add gamma table support to DSS dispc

Add gamma table support to DSS dispc.

DSS driver initializes the default gamma table at component bind time
and holds a copy of all gamma tables in its internal data structure.

Each call to dispc_mgr_set_gamma() updates the internal table and
triggers write to the HW, if it is enabled. The tables are restored to
HW in PM resume callback. The drivers internal data structure match
the HW tables in size and in number of significant bits per color
component. The dispc_mgr_set_gamma() converts the size of any given
table for the internal data structure using linear interpolation.
Default gamma table is restored if NULL is given in place of gamma
lut.

dispc_mgr_gamma_size() gives HW gamma table size for the channel and
returns 0 if gamma table is not supported by the HW or the DSS driver.

Signed-off-by: Jyri Sarha <jsarha@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>

authored by

Jyri Sarha and committed by
Tomi Valkeinen
acc3a231 f8ed34ac

+197 -22
+187 -16
drivers/gpu/drm/omapdrm/dss/dispc.c
··· 112 112 * never both, we can just use this flag for now. 113 113 */ 114 114 bool reverse_ilace_field_order:1; 115 + 116 + bool has_gamma_table:1; 115 117 }; 116 118 117 119 #define DISPC_MAX_NR_FIFOS 5 120 + #define DISPC_MAX_CHANNEL_GAMMA 4 118 121 119 122 static struct { 120 123 struct platform_device *pdev; ··· 136 133 137 134 bool ctx_valid; 138 135 u32 ctx[DISPC_SZ_REGS / sizeof(u32)]; 136 + 137 + u32 *gamma_table[DISPC_MAX_CHANNEL_GAMMA]; 139 138 140 139 const struct dispc_features *feat; 141 140 ··· 182 177 u8 low; 183 178 }; 184 179 180 + struct dispc_gamma_desc { 181 + u32 len; 182 + u32 bits; 183 + u16 reg; 184 + bool has_index; 185 + }; 186 + 185 187 static const struct { 186 188 const char *name; 187 189 u32 vsync_irq; 188 190 u32 framedone_irq; 189 191 u32 sync_lost_irq; 192 + struct dispc_gamma_desc gamma; 190 193 struct dispc_reg_field reg_desc[DISPC_MGR_FLD_NUM]; 191 194 } mgr_desc[] = { 192 195 [OMAP_DSS_CHANNEL_LCD] = { ··· 202 189 .vsync_irq = DISPC_IRQ_VSYNC, 203 190 .framedone_irq = DISPC_IRQ_FRAMEDONE, 204 191 .sync_lost_irq = DISPC_IRQ_SYNC_LOST, 192 + .gamma = { 193 + .len = 256, 194 + .bits = 8, 195 + .reg = DISPC_GAMMA_TABLE0, 196 + .has_index = true, 197 + }, 205 198 .reg_desc = { 206 199 [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL, 0, 0 }, 207 200 [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL, 3, 3 }, ··· 225 206 .vsync_irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN, 226 207 .framedone_irq = DISPC_IRQ_FRAMEDONETV, 227 208 .sync_lost_irq = DISPC_IRQ_SYNC_LOST_DIGIT, 209 + .gamma = { 210 + .len = 1024, 211 + .bits = 10, 212 + .reg = DISPC_GAMMA_TABLE2, 213 + .has_index = false, 214 + }, 228 215 .reg_desc = { 229 216 [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL, 1, 1 }, 230 217 [DISPC_MGR_FLD_STNTFT] = { }, ··· 248 223 .vsync_irq = DISPC_IRQ_VSYNC2, 249 224 .framedone_irq = DISPC_IRQ_FRAMEDONE2, 250 225 .sync_lost_irq = DISPC_IRQ_SYNC_LOST2, 226 + .gamma = { 227 + .len = 256, 228 + .bits = 8, 229 + .reg = DISPC_GAMMA_TABLE1, 230 + .has_index = true, 231 + }, 251 232 .reg_desc = { 252 233 [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL2, 0, 0 }, 253 234 [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL2, 3, 3 }, ··· 271 240 .vsync_irq = DISPC_IRQ_VSYNC3, 272 241 .framedone_irq = DISPC_IRQ_FRAMEDONE3, 273 242 .sync_lost_irq = DISPC_IRQ_SYNC_LOST3, 243 + .gamma = { 244 + .len = 256, 245 + .bits = 8, 246 + .reg = DISPC_GAMMA_TABLE3, 247 + .has_index = true, 248 + }, 274 249 .reg_desc = { 275 250 [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL3, 0, 0 }, 276 251 [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL3, 3, 3 }, ··· 1118 1081 unsigned unit = dss_feat_get_burst_size_unit(); 1119 1082 /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */ 1120 1083 return unit * 8; 1121 - } 1122 - 1123 - void dispc_enable_gamma_table(bool enable) 1124 - { 1125 - /* 1126 - * This is partially implemented to support only disabling of 1127 - * the gamma table. 1128 - */ 1129 - if (enable) { 1130 - DSSWARN("Gamma table enabling for TV not yet supported"); 1131 - return; 1132 - } 1133 - 1134 - REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9); 1135 1084 } 1136 1085 1137 1086 static void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable) ··· 3813 3790 REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3); /* SIDLEMODE: no idle */ 3814 3791 } 3815 3792 3793 + u32 dispc_mgr_gamma_size(enum omap_channel channel) 3794 + { 3795 + const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3796 + 3797 + if (!dispc.feat->has_gamma_table) 3798 + return 0; 3799 + 3800 + return gdesc->len; 3801 + } 3802 + EXPORT_SYMBOL(dispc_mgr_gamma_size); 3803 + 3804 + static void dispc_mgr_write_gamma_table(enum omap_channel channel) 3805 + { 3806 + const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3807 + u32 *table = dispc.gamma_table[channel]; 3808 + unsigned int i; 3809 + 3810 + DSSDBG("%s: channel %d\n", __func__, channel); 3811 + 3812 + for (i = 0; i < gdesc->len; ++i) { 3813 + u32 v = table[i]; 3814 + 3815 + if (gdesc->has_index) 3816 + v |= i << 24; 3817 + else if (i == 0) 3818 + v |= 1 << 31; 3819 + 3820 + dispc_write_reg(gdesc->reg, v); 3821 + } 3822 + } 3823 + 3824 + static void dispc_restore_gamma_tables(void) 3825 + { 3826 + DSSDBG("%s()\n", __func__); 3827 + 3828 + if (!dispc.feat->has_gamma_table) 3829 + return; 3830 + 3831 + dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD); 3832 + 3833 + dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_DIGIT); 3834 + 3835 + if (dss_has_feature(FEAT_MGR_LCD2)) 3836 + dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD2); 3837 + 3838 + if (dss_has_feature(FEAT_MGR_LCD3)) 3839 + dispc_mgr_write_gamma_table(OMAP_DSS_CHANNEL_LCD3); 3840 + } 3841 + 3842 + static const struct drm_color_lut dispc_mgr_gamma_default_lut[] = { 3843 + { .red = 0, .green = 0, .blue = 0, }, 3844 + { .red = U16_MAX, .green = U16_MAX, .blue = U16_MAX, }, 3845 + }; 3846 + 3847 + void dispc_mgr_set_gamma(enum omap_channel channel, 3848 + const struct drm_color_lut *lut, 3849 + unsigned int length) 3850 + { 3851 + const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3852 + u32 *table = dispc.gamma_table[channel]; 3853 + uint i; 3854 + 3855 + DSSDBG("%s: channel %d, lut len %u, hw len %u\n", __func__, 3856 + channel, length, gdesc->len); 3857 + 3858 + if (!dispc.feat->has_gamma_table) 3859 + return; 3860 + 3861 + if (lut == NULL || length < 2) { 3862 + lut = dispc_mgr_gamma_default_lut; 3863 + length = ARRAY_SIZE(dispc_mgr_gamma_default_lut); 3864 + } 3865 + 3866 + for (i = 0; i < length - 1; ++i) { 3867 + uint first = i * (gdesc->len - 1) / (length - 1); 3868 + uint last = (i + 1) * (gdesc->len - 1) / (length - 1); 3869 + uint w = last - first; 3870 + u16 r, g, b; 3871 + uint j; 3872 + 3873 + if (w == 0) 3874 + continue; 3875 + 3876 + for (j = 0; j <= w; j++) { 3877 + r = (lut[i].red * (w - j) + lut[i+1].red * j) / w; 3878 + g = (lut[i].green * (w - j) + lut[i+1].green * j) / w; 3879 + b = (lut[i].blue * (w - j) + lut[i+1].blue * j) / w; 3880 + 3881 + r >>= 16 - gdesc->bits; 3882 + g >>= 16 - gdesc->bits; 3883 + b >>= 16 - gdesc->bits; 3884 + 3885 + table[first + j] = (r << (gdesc->bits * 2)) | 3886 + (g << gdesc->bits) | b; 3887 + } 3888 + } 3889 + 3890 + if (dispc.is_enabled) 3891 + dispc_mgr_write_gamma_table(channel); 3892 + } 3893 + EXPORT_SYMBOL(dispc_mgr_set_gamma); 3894 + 3895 + static int dispc_init_gamma_tables(void) 3896 + { 3897 + int channel; 3898 + 3899 + if (!dispc.feat->has_gamma_table) 3900 + return 0; 3901 + 3902 + for (channel = 0; channel < ARRAY_SIZE(dispc.gamma_table); channel++) { 3903 + const struct dispc_gamma_desc *gdesc = &mgr_desc[channel].gamma; 3904 + u32 *gt; 3905 + 3906 + if (channel == OMAP_DSS_CHANNEL_LCD2 && 3907 + !dss_has_feature(FEAT_MGR_LCD2)) 3908 + continue; 3909 + 3910 + if (channel == OMAP_DSS_CHANNEL_LCD3 && 3911 + !dss_has_feature(FEAT_MGR_LCD3)) 3912 + continue; 3913 + 3914 + gt = devm_kmalloc_array(&dispc.pdev->dev, gdesc->len, 3915 + sizeof(u32), GFP_KERNEL); 3916 + if (!gt) 3917 + return -ENOMEM; 3918 + 3919 + dispc.gamma_table[channel] = gt; 3920 + 3921 + dispc_mgr_set_gamma(channel, NULL, 0); 3922 + } 3923 + return 0; 3924 + } 3925 + 3816 3926 static void _omap_dispc_initial_config(void) 3817 3927 { 3818 3928 u32 l; ··· 3961 3805 dispc.core_clk_rate = dispc_fclk_rate(); 3962 3806 } 3963 3807 3964 - /* FUNCGATED */ 3965 - if (dss_has_feature(FEAT_FUNCGATED)) 3808 + /* Use gamma table mode, instead of palette mode */ 3809 + if (dispc.feat->has_gamma_table) 3810 + REG_FLD_MOD(DISPC_CONFIG, 1, 3, 3); 3811 + 3812 + /* For older DSS versions (FEAT_FUNCGATED) this enables 3813 + * func-clock auto-gating. For newer versions 3814 + * (dispc.feat->has_gamma_table) this enables tv-out gamma tables. 3815 + */ 3816 + if (dss_has_feature(FEAT_FUNCGATED) || dispc.feat->has_gamma_table) 3966 3817 REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); 3967 3818 3968 3819 dispc_setup_color_conv_coef(); ··· 4073 3910 .has_writeback = true, 4074 3911 .supports_double_pixel = true, 4075 3912 .reverse_ilace_field_order = true, 3913 + .has_gamma_table = true, 4076 3914 }; 4077 3915 4078 3916 static const struct dispc_features omap54xx_dispc_feats = { ··· 4099 3935 .has_writeback = true, 4100 3936 .supports_double_pixel = true, 4101 3937 .reverse_ilace_field_order = true, 3938 + .has_gamma_table = true, 4102 3939 }; 4103 3940 4104 3941 static int dispc_init_features(struct platform_device *pdev) ··· 4241 4076 } 4242 4077 } 4243 4078 4079 + r = dispc_init_gamma_tables(); 4080 + if (r) 4081 + return r; 4082 + 4244 4083 pm_runtime_enable(&pdev->dev); 4245 4084 4246 4085 r = dispc_runtime_get(); ··· 4315 4146 _omap_dispc_initial_config(); 4316 4147 4317 4148 dispc_restore_context(); 4149 + 4150 + dispc_restore_gamma_tables(); 4318 4151 } 4319 4152 4320 4153 dispc.is_enabled = true;
+5
drivers/gpu/drm/omapdrm/dss/dispc.h
··· 42 42 #define DISPC_MSTANDBY_CTRL 0x0858 43 43 #define DISPC_GLOBAL_MFLAG_ATTRIBUTE 0x085C 44 44 45 + #define DISPC_GAMMA_TABLE0 0x0630 46 + #define DISPC_GAMMA_TABLE1 0x0634 47 + #define DISPC_GAMMA_TABLE2 0x0638 48 + #define DISPC_GAMMA_TABLE3 0x0850 49 + 45 50 /* DISPC overlay registers */ 46 51 #define DISPC_OVL_BA0(n) (DISPC_OVL_BASE(n) + \ 47 52 DISPC_BA0_OFFSET(n))
-3
drivers/gpu/drm/omapdrm/dss/hdmi4.c
··· 208 208 209 209 hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); 210 210 211 - /* bypass TV gamma table */ 212 - dispc_enable_gamma_table(0); 213 - 214 211 /* tv size */ 215 212 dss_mgr_set_timings(channel, p); 216 213
-3
drivers/gpu/drm/omapdrm/dss/hdmi5.c
··· 226 226 227 227 hdmi5_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); 228 228 229 - /* bypass TV gamma table */ 230 - dispc_enable_gamma_table(0); 231 - 232 229 /* tv size */ 233 230 dss_mgr_set_timings(channel, p); 234 231
+5
drivers/gpu/drm/omapdrm/dss/omapdss.h
··· 24 24 #include <linux/interrupt.h> 25 25 #include <video/videomode.h> 26 26 #include <linux/platform_data/omapdss.h> 27 + #include <uapi/drm/drm_mode.h> 27 28 28 29 #define DISPC_IRQ_FRAMEDONE (1 << 0) 29 30 #define DISPC_IRQ_VSYNC (1 << 1) ··· 909 908 const struct omap_video_timings *timings); 910 909 void dispc_mgr_setup(enum omap_channel channel, 911 910 const struct omap_overlay_manager_info *info); 911 + u32 dispc_mgr_gamma_size(enum omap_channel channel); 912 + void dispc_mgr_set_gamma(enum omap_channel channel, 913 + const struct drm_color_lut *lut, 914 + unsigned int length); 912 915 913 916 int dispc_ovl_enable(enum omap_plane plane, bool enable); 914 917 bool dispc_ovl_enabled(enum omap_plane plane);