1ba2d53fbSBenjamin Gaignard /* 2ba2d53fbSBenjamin Gaignard * Copyright (C) STMicroelectronics SA 2014 3ba2d53fbSBenjamin Gaignard * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> 4ba2d53fbSBenjamin Gaignard * Fabien Dessenne <fabien.dessenne@st.com> 5ba2d53fbSBenjamin Gaignard * for STMicroelectronics. 6ba2d53fbSBenjamin Gaignard * License terms: GNU General Public License (GPL), version 2 7ba2d53fbSBenjamin Gaignard */ 8ba2d53fbSBenjamin Gaignard 9ba2d53fbSBenjamin Gaignard #include <linux/clk.h> 10ba2d53fbSBenjamin Gaignard #include <linux/dma-mapping.h> 11ba2d53fbSBenjamin Gaignard 12d219673dSBenjamin Gaignard #include "sti_compositor.h" 13871bcdfeSVincent Abriou #include "sti_drm_plane.h" 14ba2d53fbSBenjamin Gaignard #include "sti_gdp.h" 15ba2d53fbSBenjamin Gaignard #include "sti_vtg.h" 16ba2d53fbSBenjamin Gaignard 174af6b12aSBenjamin Gaignard #define ALPHASWITCH BIT(6) 18ba2d53fbSBenjamin Gaignard #define ENA_COLOR_FILL BIT(8) 194af6b12aSBenjamin Gaignard #define BIGNOTLITTLE BIT(23) 20ba2d53fbSBenjamin Gaignard #define WAIT_NEXT_VSYNC BIT(31) 21ba2d53fbSBenjamin Gaignard 22ba2d53fbSBenjamin Gaignard /* GDP color formats */ 23ba2d53fbSBenjamin Gaignard #define GDP_RGB565 0x00 24ba2d53fbSBenjamin Gaignard #define GDP_RGB888 0x01 25ba2d53fbSBenjamin Gaignard #define GDP_RGB888_32 0x02 268adb5776SFabien Dessenne #define GDP_XBGR8888 (GDP_RGB888_32 | BIGNOTLITTLE | ALPHASWITCH) 27ba2d53fbSBenjamin Gaignard #define GDP_ARGB8565 0x04 28ba2d53fbSBenjamin Gaignard #define GDP_ARGB8888 0x05 294af6b12aSBenjamin Gaignard #define GDP_ABGR8888 (GDP_ARGB8888 | BIGNOTLITTLE | ALPHASWITCH) 30ba2d53fbSBenjamin Gaignard #define GDP_ARGB1555 0x06 31ba2d53fbSBenjamin Gaignard #define GDP_ARGB4444 0x07 32ba2d53fbSBenjamin Gaignard #define GDP_CLUT8 0x0B 33ba2d53fbSBenjamin Gaignard #define GDP_YCBR888 0x10 34ba2d53fbSBenjamin Gaignard #define GDP_YCBR422R 0x12 35ba2d53fbSBenjamin Gaignard #define GDP_AYCBR8888 0x15 36ba2d53fbSBenjamin Gaignard 37ba2d53fbSBenjamin Gaignard #define GAM_GDP_CTL_OFFSET 0x00 38ba2d53fbSBenjamin Gaignard #define GAM_GDP_AGC_OFFSET 0x04 39ba2d53fbSBenjamin Gaignard #define GAM_GDP_VPO_OFFSET 0x0C 40ba2d53fbSBenjamin Gaignard #define GAM_GDP_VPS_OFFSET 0x10 41ba2d53fbSBenjamin Gaignard #define GAM_GDP_PML_OFFSET 0x14 42ba2d53fbSBenjamin Gaignard #define GAM_GDP_PMP_OFFSET 0x18 43ba2d53fbSBenjamin Gaignard #define GAM_GDP_SIZE_OFFSET 0x1C 44ba2d53fbSBenjamin Gaignard #define GAM_GDP_NVN_OFFSET 0x24 45ba2d53fbSBenjamin Gaignard #define GAM_GDP_KEY1_OFFSET 0x28 46ba2d53fbSBenjamin Gaignard #define GAM_GDP_KEY2_OFFSET 0x2C 47ba2d53fbSBenjamin Gaignard #define GAM_GDP_PPT_OFFSET 0x34 48ba2d53fbSBenjamin Gaignard #define GAM_GDP_CML_OFFSET 0x3C 49ba2d53fbSBenjamin Gaignard #define GAM_GDP_MST_OFFSET 0x68 50ba2d53fbSBenjamin Gaignard 51ba2d53fbSBenjamin Gaignard #define GAM_GDP_ALPHARANGE_255 BIT(5) 52ba2d53fbSBenjamin Gaignard #define GAM_GDP_AGC_FULL_RANGE 0x00808080 53ba2d53fbSBenjamin Gaignard #define GAM_GDP_PPT_IGNORE (BIT(1) | BIT(0)) 54ba2d53fbSBenjamin Gaignard #define GAM_GDP_SIZE_MAX 0x7FF 55ba2d53fbSBenjamin Gaignard 56ba2d53fbSBenjamin Gaignard #define GDP_NODE_NB_BANK 2 57ba2d53fbSBenjamin Gaignard #define GDP_NODE_PER_FIELD 2 58ba2d53fbSBenjamin Gaignard 59ba2d53fbSBenjamin Gaignard struct sti_gdp_node { 60ba2d53fbSBenjamin Gaignard u32 gam_gdp_ctl; 61ba2d53fbSBenjamin Gaignard u32 gam_gdp_agc; 62ba2d53fbSBenjamin Gaignard u32 reserved1; 63ba2d53fbSBenjamin Gaignard u32 gam_gdp_vpo; 64ba2d53fbSBenjamin Gaignard u32 gam_gdp_vps; 65ba2d53fbSBenjamin Gaignard u32 gam_gdp_pml; 66ba2d53fbSBenjamin Gaignard u32 gam_gdp_pmp; 67ba2d53fbSBenjamin Gaignard u32 gam_gdp_size; 68ba2d53fbSBenjamin Gaignard u32 reserved2; 69ba2d53fbSBenjamin Gaignard u32 gam_gdp_nvn; 70ba2d53fbSBenjamin Gaignard u32 gam_gdp_key1; 71ba2d53fbSBenjamin Gaignard u32 gam_gdp_key2; 72ba2d53fbSBenjamin Gaignard u32 reserved3; 73ba2d53fbSBenjamin Gaignard u32 gam_gdp_ppt; 74ba2d53fbSBenjamin Gaignard u32 reserved4; 75ba2d53fbSBenjamin Gaignard u32 gam_gdp_cml; 76ba2d53fbSBenjamin Gaignard }; 77ba2d53fbSBenjamin Gaignard 78ba2d53fbSBenjamin Gaignard struct sti_gdp_node_list { 79ba2d53fbSBenjamin Gaignard struct sti_gdp_node *top_field; 80a51fe84dSBenjamin Gaignard dma_addr_t top_field_paddr; 81ba2d53fbSBenjamin Gaignard struct sti_gdp_node *btm_field; 82a51fe84dSBenjamin Gaignard dma_addr_t btm_field_paddr; 83ba2d53fbSBenjamin Gaignard }; 84ba2d53fbSBenjamin Gaignard 85ba2d53fbSBenjamin Gaignard /** 86ba2d53fbSBenjamin Gaignard * STI GDP structure 87ba2d53fbSBenjamin Gaignard * 88871bcdfeSVincent Abriou * @sti_plane: sti_plane structure 89871bcdfeSVincent Abriou * @dev: driver device 90871bcdfeSVincent Abriou * @regs: gdp registers 91ba2d53fbSBenjamin Gaignard * @clk_pix: pixel clock for the current gdp 925e03abc5SBenjamin Gaignard * @clk_main_parent: gdp parent clock if main path used 935e03abc5SBenjamin Gaignard * @clk_aux_parent: gdp parent clock if aux path used 94ba2d53fbSBenjamin Gaignard * @vtg_field_nb: callback for VTG FIELD (top or bottom) notification 95ba2d53fbSBenjamin Gaignard * @is_curr_top: true if the current node processed is the top field 96ba2d53fbSBenjamin Gaignard * @node_list: array of node list 97ba2d53fbSBenjamin Gaignard */ 98ba2d53fbSBenjamin Gaignard struct sti_gdp { 99871bcdfeSVincent Abriou struct sti_plane plane; 100871bcdfeSVincent Abriou struct device *dev; 101871bcdfeSVincent Abriou void __iomem *regs; 102ba2d53fbSBenjamin Gaignard struct clk *clk_pix; 1035e03abc5SBenjamin Gaignard struct clk *clk_main_parent; 1045e03abc5SBenjamin Gaignard struct clk *clk_aux_parent; 105ba2d53fbSBenjamin Gaignard struct notifier_block vtg_field_nb; 106ba2d53fbSBenjamin Gaignard bool is_curr_top; 107ba2d53fbSBenjamin Gaignard struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK]; 108ba2d53fbSBenjamin Gaignard }; 109ba2d53fbSBenjamin Gaignard 110871bcdfeSVincent Abriou #define to_sti_gdp(x) container_of(x, struct sti_gdp, plane) 111ba2d53fbSBenjamin Gaignard 112ba2d53fbSBenjamin Gaignard static const uint32_t gdp_supported_formats[] = { 113ba2d53fbSBenjamin Gaignard DRM_FORMAT_XRGB8888, 1148adb5776SFabien Dessenne DRM_FORMAT_XBGR8888, 115ba2d53fbSBenjamin Gaignard DRM_FORMAT_ARGB8888, 1164af6b12aSBenjamin Gaignard DRM_FORMAT_ABGR8888, 117ba2d53fbSBenjamin Gaignard DRM_FORMAT_ARGB4444, 118ba2d53fbSBenjamin Gaignard DRM_FORMAT_ARGB1555, 119ba2d53fbSBenjamin Gaignard DRM_FORMAT_RGB565, 120ba2d53fbSBenjamin Gaignard DRM_FORMAT_RGB888, 121ba2d53fbSBenjamin Gaignard DRM_FORMAT_AYUV, 122ba2d53fbSBenjamin Gaignard DRM_FORMAT_YUV444, 123ba2d53fbSBenjamin Gaignard DRM_FORMAT_VYUY, 124ba2d53fbSBenjamin Gaignard DRM_FORMAT_C8, 125ba2d53fbSBenjamin Gaignard }; 126ba2d53fbSBenjamin Gaignard 127871bcdfeSVincent Abriou static const uint32_t *sti_gdp_get_formats(struct sti_plane *plane) 128ba2d53fbSBenjamin Gaignard { 129ba2d53fbSBenjamin Gaignard return gdp_supported_formats; 130ba2d53fbSBenjamin Gaignard } 131ba2d53fbSBenjamin Gaignard 132871bcdfeSVincent Abriou static unsigned int sti_gdp_get_nb_formats(struct sti_plane *plane) 133ba2d53fbSBenjamin Gaignard { 134ba2d53fbSBenjamin Gaignard return ARRAY_SIZE(gdp_supported_formats); 135ba2d53fbSBenjamin Gaignard } 136ba2d53fbSBenjamin Gaignard 137ba2d53fbSBenjamin Gaignard static int sti_gdp_fourcc2format(int fourcc) 138ba2d53fbSBenjamin Gaignard { 139ba2d53fbSBenjamin Gaignard switch (fourcc) { 140ba2d53fbSBenjamin Gaignard case DRM_FORMAT_XRGB8888: 141ba2d53fbSBenjamin Gaignard return GDP_RGB888_32; 1428adb5776SFabien Dessenne case DRM_FORMAT_XBGR8888: 1438adb5776SFabien Dessenne return GDP_XBGR8888; 144ba2d53fbSBenjamin Gaignard case DRM_FORMAT_ARGB8888: 145ba2d53fbSBenjamin Gaignard return GDP_ARGB8888; 1464af6b12aSBenjamin Gaignard case DRM_FORMAT_ABGR8888: 1474af6b12aSBenjamin Gaignard return GDP_ABGR8888; 148ba2d53fbSBenjamin Gaignard case DRM_FORMAT_ARGB4444: 149ba2d53fbSBenjamin Gaignard return GDP_ARGB4444; 150ba2d53fbSBenjamin Gaignard case DRM_FORMAT_ARGB1555: 151ba2d53fbSBenjamin Gaignard return GDP_ARGB1555; 152ba2d53fbSBenjamin Gaignard case DRM_FORMAT_RGB565: 153ba2d53fbSBenjamin Gaignard return GDP_RGB565; 154ba2d53fbSBenjamin Gaignard case DRM_FORMAT_RGB888: 155ba2d53fbSBenjamin Gaignard return GDP_RGB888; 156ba2d53fbSBenjamin Gaignard case DRM_FORMAT_AYUV: 157ba2d53fbSBenjamin Gaignard return GDP_AYCBR8888; 158ba2d53fbSBenjamin Gaignard case DRM_FORMAT_YUV444: 159ba2d53fbSBenjamin Gaignard return GDP_YCBR888; 160ba2d53fbSBenjamin Gaignard case DRM_FORMAT_VYUY: 161ba2d53fbSBenjamin Gaignard return GDP_YCBR422R; 162ba2d53fbSBenjamin Gaignard case DRM_FORMAT_C8: 163ba2d53fbSBenjamin Gaignard return GDP_CLUT8; 164ba2d53fbSBenjamin Gaignard } 165ba2d53fbSBenjamin Gaignard return -1; 166ba2d53fbSBenjamin Gaignard } 167ba2d53fbSBenjamin Gaignard 168ba2d53fbSBenjamin Gaignard static int sti_gdp_get_alpharange(int format) 169ba2d53fbSBenjamin Gaignard { 170ba2d53fbSBenjamin Gaignard switch (format) { 171ba2d53fbSBenjamin Gaignard case GDP_ARGB8565: 172ba2d53fbSBenjamin Gaignard case GDP_ARGB8888: 173ba2d53fbSBenjamin Gaignard case GDP_AYCBR8888: 1744af6b12aSBenjamin Gaignard case GDP_ABGR8888: 175ba2d53fbSBenjamin Gaignard return GAM_GDP_ALPHARANGE_255; 176ba2d53fbSBenjamin Gaignard } 177ba2d53fbSBenjamin Gaignard return 0; 178ba2d53fbSBenjamin Gaignard } 179ba2d53fbSBenjamin Gaignard 180ba2d53fbSBenjamin Gaignard /** 181ba2d53fbSBenjamin Gaignard * sti_gdp_get_free_nodes 182871bcdfeSVincent Abriou * @plane: gdp plane 183ba2d53fbSBenjamin Gaignard * 184ba2d53fbSBenjamin Gaignard * Look for a GDP node list that is not currently read by the HW. 185ba2d53fbSBenjamin Gaignard * 186ba2d53fbSBenjamin Gaignard * RETURNS: 187ba2d53fbSBenjamin Gaignard * Pointer to the free GDP node list 188ba2d53fbSBenjamin Gaignard */ 189871bcdfeSVincent Abriou static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_plane *plane) 190ba2d53fbSBenjamin Gaignard { 191ba2d53fbSBenjamin Gaignard int hw_nvn; 192871bcdfeSVincent Abriou struct sti_gdp *gdp = to_sti_gdp(plane); 193ba2d53fbSBenjamin Gaignard unsigned int i; 194ba2d53fbSBenjamin Gaignard 195871bcdfeSVincent Abriou hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET); 196ba2d53fbSBenjamin Gaignard if (!hw_nvn) 197ba2d53fbSBenjamin Gaignard goto end; 198ba2d53fbSBenjamin Gaignard 199ba2d53fbSBenjamin Gaignard for (i = 0; i < GDP_NODE_NB_BANK; i++) 200a51fe84dSBenjamin Gaignard if ((hw_nvn != gdp->node_list[i].btm_field_paddr) && 201a51fe84dSBenjamin Gaignard (hw_nvn != gdp->node_list[i].top_field_paddr)) 202ba2d53fbSBenjamin Gaignard return &gdp->node_list[i]; 203ba2d53fbSBenjamin Gaignard 204d219673dSBenjamin Gaignard /* in hazardious cases restart with the first node */ 205d219673dSBenjamin Gaignard DRM_ERROR("inconsistent NVN for %s: 0x%08X\n", 206871bcdfeSVincent Abriou sti_plane_to_str(plane), hw_nvn); 207d219673dSBenjamin Gaignard 208ba2d53fbSBenjamin Gaignard end: 209ba2d53fbSBenjamin Gaignard return &gdp->node_list[0]; 210ba2d53fbSBenjamin Gaignard } 211ba2d53fbSBenjamin Gaignard 212ba2d53fbSBenjamin Gaignard /** 213ba2d53fbSBenjamin Gaignard * sti_gdp_get_current_nodes 214871bcdfeSVincent Abriou * @plane: GDP plane 215ba2d53fbSBenjamin Gaignard * 216ba2d53fbSBenjamin Gaignard * Look for GDP nodes that are currently read by the HW. 217ba2d53fbSBenjamin Gaignard * 218ba2d53fbSBenjamin Gaignard * RETURNS: 219ba2d53fbSBenjamin Gaignard * Pointer to the current GDP node list 220ba2d53fbSBenjamin Gaignard */ 221ba2d53fbSBenjamin Gaignard static 222871bcdfeSVincent Abriou struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_plane *plane) 223ba2d53fbSBenjamin Gaignard { 224ba2d53fbSBenjamin Gaignard int hw_nvn; 225871bcdfeSVincent Abriou struct sti_gdp *gdp = to_sti_gdp(plane); 226ba2d53fbSBenjamin Gaignard unsigned int i; 227ba2d53fbSBenjamin Gaignard 228871bcdfeSVincent Abriou hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET); 229ba2d53fbSBenjamin Gaignard if (!hw_nvn) 230ba2d53fbSBenjamin Gaignard goto end; 231ba2d53fbSBenjamin Gaignard 232ba2d53fbSBenjamin Gaignard for (i = 0; i < GDP_NODE_NB_BANK; i++) 233a51fe84dSBenjamin Gaignard if ((hw_nvn == gdp->node_list[i].btm_field_paddr) || 234a51fe84dSBenjamin Gaignard (hw_nvn == gdp->node_list[i].top_field_paddr)) 235ba2d53fbSBenjamin Gaignard return &gdp->node_list[i]; 236ba2d53fbSBenjamin Gaignard 237ba2d53fbSBenjamin Gaignard end: 238d219673dSBenjamin Gaignard DRM_DEBUG_DRIVER("Warning, NVN 0x%08X for %s does not match any node\n", 239871bcdfeSVincent Abriou hw_nvn, sti_plane_to_str(plane)); 240d219673dSBenjamin Gaignard 241ba2d53fbSBenjamin Gaignard return NULL; 242ba2d53fbSBenjamin Gaignard } 243ba2d53fbSBenjamin Gaignard 244ba2d53fbSBenjamin Gaignard /** 245871bcdfeSVincent Abriou * sti_gdp_prepare 246871bcdfeSVincent Abriou * @plane: gdp plane 247ba2d53fbSBenjamin Gaignard * @first_prepare: true if it is the first time this function is called 248ba2d53fbSBenjamin Gaignard * 249871bcdfeSVincent Abriou * Update the free GDP node list according to the plane properties. 250ba2d53fbSBenjamin Gaignard * 251ba2d53fbSBenjamin Gaignard * RETURNS: 252ba2d53fbSBenjamin Gaignard * 0 on success. 253ba2d53fbSBenjamin Gaignard */ 254871bcdfeSVincent Abriou static int sti_gdp_prepare(struct sti_plane *plane, bool first_prepare) 255ba2d53fbSBenjamin Gaignard { 256ba2d53fbSBenjamin Gaignard struct sti_gdp_node_list *list; 257ba2d53fbSBenjamin Gaignard struct sti_gdp_node *top_field, *btm_field; 258871bcdfeSVincent Abriou struct drm_display_mode *mode = plane->mode; 259871bcdfeSVincent Abriou struct sti_gdp *gdp = to_sti_gdp(plane); 260871bcdfeSVincent Abriou struct device *dev = gdp->dev; 261d219673dSBenjamin Gaignard struct sti_compositor *compo = dev_get_drvdata(dev); 262ba2d53fbSBenjamin Gaignard int format; 263ba2d53fbSBenjamin Gaignard unsigned int depth, bpp; 264ba2d53fbSBenjamin Gaignard int rate = mode->clock * 1000; 265ba2d53fbSBenjamin Gaignard int res; 266ba2d53fbSBenjamin Gaignard u32 ydo, xdo, yds, xds; 267ba2d53fbSBenjamin Gaignard 268871bcdfeSVincent Abriou list = sti_gdp_get_free_nodes(plane); 269ba2d53fbSBenjamin Gaignard top_field = list->top_field; 270ba2d53fbSBenjamin Gaignard btm_field = list->btm_field; 271ba2d53fbSBenjamin Gaignard 272d219673dSBenjamin Gaignard dev_dbg(dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__, 273871bcdfeSVincent Abriou sti_plane_to_str(plane), top_field, btm_field); 274d219673dSBenjamin Gaignard 275871bcdfeSVincent Abriou /* Build the top field from plane params */ 276ba2d53fbSBenjamin Gaignard top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE; 277ba2d53fbSBenjamin Gaignard top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC; 278871bcdfeSVincent Abriou format = sti_gdp_fourcc2format(plane->format); 279ba2d53fbSBenjamin Gaignard if (format == -1) { 280ba2d53fbSBenjamin Gaignard DRM_ERROR("Format not supported by GDP %.4s\n", 281871bcdfeSVincent Abriou (char *)&plane->format); 282ba2d53fbSBenjamin Gaignard return 1; 283ba2d53fbSBenjamin Gaignard } 284ba2d53fbSBenjamin Gaignard top_field->gam_gdp_ctl |= format; 285ba2d53fbSBenjamin Gaignard top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format); 286ba2d53fbSBenjamin Gaignard top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE; 287ba2d53fbSBenjamin Gaignard 288ba2d53fbSBenjamin Gaignard /* pixel memory location */ 289871bcdfeSVincent Abriou drm_fb_get_bpp_depth(plane->format, &depth, &bpp); 290871bcdfeSVincent Abriou top_field->gam_gdp_pml = (u32)plane->paddr + plane->offsets[0]; 291871bcdfeSVincent Abriou top_field->gam_gdp_pml += plane->src_x * (bpp >> 3); 292871bcdfeSVincent Abriou top_field->gam_gdp_pml += plane->src_y * plane->pitches[0]; 293ba2d53fbSBenjamin Gaignard 294ba2d53fbSBenjamin Gaignard /* input parameters */ 295871bcdfeSVincent Abriou top_field->gam_gdp_pmp = plane->pitches[0]; 296ba2d53fbSBenjamin Gaignard top_field->gam_gdp_size = 297871bcdfeSVincent Abriou clamp_val(plane->src_h, 0, GAM_GDP_SIZE_MAX) << 16 | 298871bcdfeSVincent Abriou clamp_val(plane->src_w, 0, GAM_GDP_SIZE_MAX); 299ba2d53fbSBenjamin Gaignard 300ba2d53fbSBenjamin Gaignard /* output parameters */ 301871bcdfeSVincent Abriou ydo = sti_vtg_get_line_number(*mode, plane->dst_y); 302871bcdfeSVincent Abriou yds = sti_vtg_get_line_number(*mode, plane->dst_y + plane->dst_h - 1); 303871bcdfeSVincent Abriou xdo = sti_vtg_get_pixel_number(*mode, plane->dst_x); 304871bcdfeSVincent Abriou xds = sti_vtg_get_pixel_number(*mode, plane->dst_x + plane->dst_w - 1); 305ba2d53fbSBenjamin Gaignard top_field->gam_gdp_vpo = (ydo << 16) | xdo; 306ba2d53fbSBenjamin Gaignard top_field->gam_gdp_vps = (yds << 16) | xds; 307ba2d53fbSBenjamin Gaignard 308ba2d53fbSBenjamin Gaignard /* Same content and chained together */ 309ba2d53fbSBenjamin Gaignard memcpy(btm_field, top_field, sizeof(*btm_field)); 310a51fe84dSBenjamin Gaignard top_field->gam_gdp_nvn = list->btm_field_paddr; 311a51fe84dSBenjamin Gaignard btm_field->gam_gdp_nvn = list->top_field_paddr; 312ba2d53fbSBenjamin Gaignard 313ba2d53fbSBenjamin Gaignard /* Interlaced mode */ 314871bcdfeSVincent Abriou if (plane->mode->flags & DRM_MODE_FLAG_INTERLACE) 315ba2d53fbSBenjamin Gaignard btm_field->gam_gdp_pml = top_field->gam_gdp_pml + 316871bcdfeSVincent Abriou plane->pitches[0]; 317ba2d53fbSBenjamin Gaignard 318ba2d53fbSBenjamin Gaignard if (first_prepare) { 319d219673dSBenjamin Gaignard /* Register gdp callback */ 320871bcdfeSVincent Abriou if (sti_vtg_register_client(plane->mixer_id == STI_MIXER_MAIN ? 321d219673dSBenjamin Gaignard compo->vtg_main : compo->vtg_aux, 322871bcdfeSVincent Abriou &gdp->vtg_field_nb, plane->mixer_id)) { 323d219673dSBenjamin Gaignard DRM_ERROR("Cannot register VTG notifier\n"); 324d219673dSBenjamin Gaignard return 1; 325d219673dSBenjamin Gaignard } 326d219673dSBenjamin Gaignard 327ba2d53fbSBenjamin Gaignard /* Set and enable gdp clock */ 328ba2d53fbSBenjamin Gaignard if (gdp->clk_pix) { 3295e03abc5SBenjamin Gaignard struct clk *clkp; 3305e03abc5SBenjamin Gaignard /* According to the mixer used, the gdp pixel clock 3315e03abc5SBenjamin Gaignard * should have a different parent clock. */ 332871bcdfeSVincent Abriou if (plane->mixer_id == STI_MIXER_MAIN) 3335e03abc5SBenjamin Gaignard clkp = gdp->clk_main_parent; 3345e03abc5SBenjamin Gaignard else 3355e03abc5SBenjamin Gaignard clkp = gdp->clk_aux_parent; 3365e03abc5SBenjamin Gaignard 3375e03abc5SBenjamin Gaignard if (clkp) 3385e03abc5SBenjamin Gaignard clk_set_parent(gdp->clk_pix, clkp); 3395e03abc5SBenjamin Gaignard 340ba2d53fbSBenjamin Gaignard res = clk_set_rate(gdp->clk_pix, rate); 341ba2d53fbSBenjamin Gaignard if (res < 0) { 342ba2d53fbSBenjamin Gaignard DRM_ERROR("Cannot set rate (%dHz) for gdp\n", 343ba2d53fbSBenjamin Gaignard rate); 344ba2d53fbSBenjamin Gaignard return 1; 345ba2d53fbSBenjamin Gaignard } 346ba2d53fbSBenjamin Gaignard 347ba2d53fbSBenjamin Gaignard if (clk_prepare_enable(gdp->clk_pix)) { 348ba2d53fbSBenjamin Gaignard DRM_ERROR("Failed to prepare/enable gdp\n"); 349ba2d53fbSBenjamin Gaignard return 1; 350ba2d53fbSBenjamin Gaignard } 351ba2d53fbSBenjamin Gaignard } 352ba2d53fbSBenjamin Gaignard } 353ba2d53fbSBenjamin Gaignard 354ba2d53fbSBenjamin Gaignard return 0; 355ba2d53fbSBenjamin Gaignard } 356ba2d53fbSBenjamin Gaignard 357ba2d53fbSBenjamin Gaignard /** 358871bcdfeSVincent Abriou * sti_gdp_commit 359871bcdfeSVincent Abriou * @plane: gdp plane 360ba2d53fbSBenjamin Gaignard * 361ba2d53fbSBenjamin Gaignard * Update the NVN field of the 'right' field of the current GDP node (being 362ba2d53fbSBenjamin Gaignard * used by the HW) with the address of the updated ('free') top field GDP node. 363ba2d53fbSBenjamin Gaignard * - In interlaced mode the 'right' field is the bottom field as we update 364ba2d53fbSBenjamin Gaignard * frames starting from their top field 365ba2d53fbSBenjamin Gaignard * - In progressive mode, we update both bottom and top fields which are 366ba2d53fbSBenjamin Gaignard * equal nodes. 367ba2d53fbSBenjamin Gaignard * At the next VSYNC, the updated node list will be used by the HW. 368ba2d53fbSBenjamin Gaignard * 369ba2d53fbSBenjamin Gaignard * RETURNS: 370ba2d53fbSBenjamin Gaignard * 0 on success. 371ba2d53fbSBenjamin Gaignard */ 372871bcdfeSVincent Abriou static int sti_gdp_commit(struct sti_plane *plane) 373ba2d53fbSBenjamin Gaignard { 374871bcdfeSVincent Abriou struct sti_gdp_node_list *updated_list = sti_gdp_get_free_nodes(plane); 375ba2d53fbSBenjamin Gaignard struct sti_gdp_node *updated_top_node = updated_list->top_field; 376ba2d53fbSBenjamin Gaignard struct sti_gdp_node *updated_btm_node = updated_list->btm_field; 377871bcdfeSVincent Abriou struct sti_gdp *gdp = to_sti_gdp(plane); 378a51fe84dSBenjamin Gaignard u32 dma_updated_top = updated_list->top_field_paddr; 379a51fe84dSBenjamin Gaignard u32 dma_updated_btm = updated_list->btm_field_paddr; 380871bcdfeSVincent Abriou struct sti_gdp_node_list *curr_list = sti_gdp_get_current_nodes(plane); 381ba2d53fbSBenjamin Gaignard 382871bcdfeSVincent Abriou dev_dbg(gdp->dev, "%s %s top/btm_node:0x%p/0x%p\n", __func__, 383871bcdfeSVincent Abriou sti_plane_to_str(plane), 384d219673dSBenjamin Gaignard updated_top_node, updated_btm_node); 385871bcdfeSVincent Abriou dev_dbg(gdp->dev, "Current NVN:0x%X\n", 386871bcdfeSVincent Abriou readl(gdp->regs + GAM_GDP_NVN_OFFSET)); 387871bcdfeSVincent Abriou dev_dbg(gdp->dev, "Posted buff: %lx current buff: %x\n", 388871bcdfeSVincent Abriou (unsigned long)plane->paddr, 389871bcdfeSVincent Abriou readl(gdp->regs + GAM_GDP_PML_OFFSET)); 390ba2d53fbSBenjamin Gaignard 391ba2d53fbSBenjamin Gaignard if (curr_list == NULL) { 392ba2d53fbSBenjamin Gaignard /* First update or invalid node should directly write in the 393ba2d53fbSBenjamin Gaignard * hw register */ 394d219673dSBenjamin Gaignard DRM_DEBUG_DRIVER("%s first update (or invalid node)", 395871bcdfeSVincent Abriou sti_plane_to_str(plane)); 396d219673dSBenjamin Gaignard 397ba2d53fbSBenjamin Gaignard writel(gdp->is_curr_top == true ? 398ba2d53fbSBenjamin Gaignard dma_updated_btm : dma_updated_top, 399871bcdfeSVincent Abriou gdp->regs + GAM_GDP_NVN_OFFSET); 400ba2d53fbSBenjamin Gaignard return 0; 401ba2d53fbSBenjamin Gaignard } 402ba2d53fbSBenjamin Gaignard 403871bcdfeSVincent Abriou if (plane->mode->flags & DRM_MODE_FLAG_INTERLACE) { 404ba2d53fbSBenjamin Gaignard if (gdp->is_curr_top == true) { 405ba2d53fbSBenjamin Gaignard /* Do not update in the middle of the frame, but 406ba2d53fbSBenjamin Gaignard * postpone the update after the bottom field has 407ba2d53fbSBenjamin Gaignard * been displayed */ 408ba2d53fbSBenjamin Gaignard curr_list->btm_field->gam_gdp_nvn = dma_updated_top; 409ba2d53fbSBenjamin Gaignard } else { 410ba2d53fbSBenjamin Gaignard /* Direct update to avoid one frame delay */ 411ba2d53fbSBenjamin Gaignard writel(dma_updated_top, 412871bcdfeSVincent Abriou gdp->regs + GAM_GDP_NVN_OFFSET); 413ba2d53fbSBenjamin Gaignard } 414ba2d53fbSBenjamin Gaignard } else { 415ba2d53fbSBenjamin Gaignard /* Direct update for progressive to avoid one frame delay */ 416871bcdfeSVincent Abriou writel(dma_updated_top, gdp->regs + GAM_GDP_NVN_OFFSET); 417ba2d53fbSBenjamin Gaignard } 418ba2d53fbSBenjamin Gaignard 419ba2d53fbSBenjamin Gaignard return 0; 420ba2d53fbSBenjamin Gaignard } 421ba2d53fbSBenjamin Gaignard 422ba2d53fbSBenjamin Gaignard /** 423871bcdfeSVincent Abriou * sti_gdp_disable 424871bcdfeSVincent Abriou * @plane: gdp plane 425ba2d53fbSBenjamin Gaignard * 426ba2d53fbSBenjamin Gaignard * Disable a GDP. 427ba2d53fbSBenjamin Gaignard * 428ba2d53fbSBenjamin Gaignard * RETURNS: 429ba2d53fbSBenjamin Gaignard * 0 on success. 430ba2d53fbSBenjamin Gaignard */ 431871bcdfeSVincent Abriou static int sti_gdp_disable(struct sti_plane *plane) 432ba2d53fbSBenjamin Gaignard { 433ba2d53fbSBenjamin Gaignard unsigned int i; 434871bcdfeSVincent Abriou struct sti_gdp *gdp = to_sti_gdp(plane); 435871bcdfeSVincent Abriou struct sti_compositor *compo = dev_get_drvdata(gdp->dev); 436d219673dSBenjamin Gaignard 437871bcdfeSVincent Abriou DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(plane)); 438ba2d53fbSBenjamin Gaignard 439ba2d53fbSBenjamin Gaignard /* Set the nodes as 'to be ignored on mixer' */ 440ba2d53fbSBenjamin Gaignard for (i = 0; i < GDP_NODE_NB_BANK; i++) { 441ba2d53fbSBenjamin Gaignard gdp->node_list[i].top_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; 442ba2d53fbSBenjamin Gaignard gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; 443ba2d53fbSBenjamin Gaignard } 444ba2d53fbSBenjamin Gaignard 445871bcdfeSVincent Abriou if (sti_vtg_unregister_client(plane->mixer_id == STI_MIXER_MAIN ? 446d219673dSBenjamin Gaignard compo->vtg_main : compo->vtg_aux, &gdp->vtg_field_nb)) 447d219673dSBenjamin Gaignard DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); 448d219673dSBenjamin Gaignard 449ba2d53fbSBenjamin Gaignard if (gdp->clk_pix) 450ba2d53fbSBenjamin Gaignard clk_disable_unprepare(gdp->clk_pix); 451ba2d53fbSBenjamin Gaignard 452ba2d53fbSBenjamin Gaignard return 0; 453ba2d53fbSBenjamin Gaignard } 454ba2d53fbSBenjamin Gaignard 455ba2d53fbSBenjamin Gaignard /** 456ba2d53fbSBenjamin Gaignard * sti_gdp_field_cb 457ba2d53fbSBenjamin Gaignard * @nb: notifier block 458ba2d53fbSBenjamin Gaignard * @event: event message 459ba2d53fbSBenjamin Gaignard * @data: private data 460ba2d53fbSBenjamin Gaignard * 461ba2d53fbSBenjamin Gaignard * Handle VTG top field and bottom field event. 462ba2d53fbSBenjamin Gaignard * 463ba2d53fbSBenjamin Gaignard * RETURNS: 464ba2d53fbSBenjamin Gaignard * 0 on success. 465ba2d53fbSBenjamin Gaignard */ 466ba2d53fbSBenjamin Gaignard int sti_gdp_field_cb(struct notifier_block *nb, 467ba2d53fbSBenjamin Gaignard unsigned long event, void *data) 468ba2d53fbSBenjamin Gaignard { 469ba2d53fbSBenjamin Gaignard struct sti_gdp *gdp = container_of(nb, struct sti_gdp, vtg_field_nb); 470ba2d53fbSBenjamin Gaignard 471ba2d53fbSBenjamin Gaignard switch (event) { 472ba2d53fbSBenjamin Gaignard case VTG_TOP_FIELD_EVENT: 473ba2d53fbSBenjamin Gaignard gdp->is_curr_top = true; 474ba2d53fbSBenjamin Gaignard break; 475ba2d53fbSBenjamin Gaignard case VTG_BOTTOM_FIELD_EVENT: 476ba2d53fbSBenjamin Gaignard gdp->is_curr_top = false; 477ba2d53fbSBenjamin Gaignard break; 478ba2d53fbSBenjamin Gaignard default: 479ba2d53fbSBenjamin Gaignard DRM_ERROR("unsupported event: %lu\n", event); 480ba2d53fbSBenjamin Gaignard break; 481ba2d53fbSBenjamin Gaignard } 482ba2d53fbSBenjamin Gaignard 483ba2d53fbSBenjamin Gaignard return 0; 484ba2d53fbSBenjamin Gaignard } 485ba2d53fbSBenjamin Gaignard 486871bcdfeSVincent Abriou static void sti_gdp_init(struct sti_gdp *gdp) 487ba2d53fbSBenjamin Gaignard { 488871bcdfeSVincent Abriou struct device_node *np = gdp->dev->of_node; 489a51fe84dSBenjamin Gaignard dma_addr_t dma_addr; 490ba2d53fbSBenjamin Gaignard void *base; 491ba2d53fbSBenjamin Gaignard unsigned int i, size; 492ba2d53fbSBenjamin Gaignard 493ba2d53fbSBenjamin Gaignard /* Allocate all the nodes within a single memory page */ 494ba2d53fbSBenjamin Gaignard size = sizeof(struct sti_gdp_node) * 495ba2d53fbSBenjamin Gaignard GDP_NODE_PER_FIELD * GDP_NODE_NB_BANK; 496871bcdfeSVincent Abriou base = dma_alloc_writecombine(gdp->dev, 497a51fe84dSBenjamin Gaignard size, &dma_addr, GFP_KERNEL | GFP_DMA); 498a51fe84dSBenjamin Gaignard 499ba2d53fbSBenjamin Gaignard if (!base) { 500ba2d53fbSBenjamin Gaignard DRM_ERROR("Failed to allocate memory for GDP node\n"); 501ba2d53fbSBenjamin Gaignard return; 502ba2d53fbSBenjamin Gaignard } 503ba2d53fbSBenjamin Gaignard memset(base, 0, size); 504ba2d53fbSBenjamin Gaignard 505ba2d53fbSBenjamin Gaignard for (i = 0; i < GDP_NODE_NB_BANK; i++) { 506a51fe84dSBenjamin Gaignard if (dma_addr & 0xF) { 507ba2d53fbSBenjamin Gaignard DRM_ERROR("Mem alignment failed\n"); 508ba2d53fbSBenjamin Gaignard return; 509ba2d53fbSBenjamin Gaignard } 510ba2d53fbSBenjamin Gaignard gdp->node_list[i].top_field = base; 511a51fe84dSBenjamin Gaignard gdp->node_list[i].top_field_paddr = dma_addr; 512a51fe84dSBenjamin Gaignard 513ba2d53fbSBenjamin Gaignard DRM_DEBUG_DRIVER("node[%d].top_field=%p\n", i, base); 514ba2d53fbSBenjamin Gaignard base += sizeof(struct sti_gdp_node); 515a51fe84dSBenjamin Gaignard dma_addr += sizeof(struct sti_gdp_node); 516ba2d53fbSBenjamin Gaignard 517a51fe84dSBenjamin Gaignard if (dma_addr & 0xF) { 518ba2d53fbSBenjamin Gaignard DRM_ERROR("Mem alignment failed\n"); 519ba2d53fbSBenjamin Gaignard return; 520ba2d53fbSBenjamin Gaignard } 521ba2d53fbSBenjamin Gaignard gdp->node_list[i].btm_field = base; 522a51fe84dSBenjamin Gaignard gdp->node_list[i].btm_field_paddr = dma_addr; 523ba2d53fbSBenjamin Gaignard DRM_DEBUG_DRIVER("node[%d].btm_field=%p\n", i, base); 524ba2d53fbSBenjamin Gaignard base += sizeof(struct sti_gdp_node); 525a51fe84dSBenjamin Gaignard dma_addr += sizeof(struct sti_gdp_node); 526ba2d53fbSBenjamin Gaignard } 527ba2d53fbSBenjamin Gaignard 528ba2d53fbSBenjamin Gaignard if (of_device_is_compatible(np, "st,stih407-compositor")) { 529ba2d53fbSBenjamin Gaignard /* GDP of STiH407 chip have its own pixel clock */ 530ba2d53fbSBenjamin Gaignard char *clk_name; 531ba2d53fbSBenjamin Gaignard 532871bcdfeSVincent Abriou switch (gdp->plane.desc) { 533ba2d53fbSBenjamin Gaignard case STI_GDP_0: 534ba2d53fbSBenjamin Gaignard clk_name = "pix_gdp1"; 535ba2d53fbSBenjamin Gaignard break; 536ba2d53fbSBenjamin Gaignard case STI_GDP_1: 537ba2d53fbSBenjamin Gaignard clk_name = "pix_gdp2"; 538ba2d53fbSBenjamin Gaignard break; 539ba2d53fbSBenjamin Gaignard case STI_GDP_2: 540ba2d53fbSBenjamin Gaignard clk_name = "pix_gdp3"; 541ba2d53fbSBenjamin Gaignard break; 542ba2d53fbSBenjamin Gaignard case STI_GDP_3: 543ba2d53fbSBenjamin Gaignard clk_name = "pix_gdp4"; 544ba2d53fbSBenjamin Gaignard break; 545ba2d53fbSBenjamin Gaignard default: 546ba2d53fbSBenjamin Gaignard DRM_ERROR("GDP id not recognized\n"); 547ba2d53fbSBenjamin Gaignard return; 548ba2d53fbSBenjamin Gaignard } 549ba2d53fbSBenjamin Gaignard 550871bcdfeSVincent Abriou gdp->clk_pix = devm_clk_get(gdp->dev, clk_name); 551ba2d53fbSBenjamin Gaignard if (IS_ERR(gdp->clk_pix)) 552ba2d53fbSBenjamin Gaignard DRM_ERROR("Cannot get %s clock\n", clk_name); 5535e03abc5SBenjamin Gaignard 554871bcdfeSVincent Abriou gdp->clk_main_parent = devm_clk_get(gdp->dev, "main_parent"); 5555e03abc5SBenjamin Gaignard if (IS_ERR(gdp->clk_main_parent)) 5565e03abc5SBenjamin Gaignard DRM_ERROR("Cannot get main_parent clock\n"); 5575e03abc5SBenjamin Gaignard 558871bcdfeSVincent Abriou gdp->clk_aux_parent = devm_clk_get(gdp->dev, "aux_parent"); 5595e03abc5SBenjamin Gaignard if (IS_ERR(gdp->clk_aux_parent)) 5605e03abc5SBenjamin Gaignard DRM_ERROR("Cannot get aux_parent clock\n"); 561ba2d53fbSBenjamin Gaignard } 562ba2d53fbSBenjamin Gaignard } 563ba2d53fbSBenjamin Gaignard 564871bcdfeSVincent Abriou static const struct sti_plane_funcs gdp_plane_ops = { 565ba2d53fbSBenjamin Gaignard .get_formats = sti_gdp_get_formats, 566ba2d53fbSBenjamin Gaignard .get_nb_formats = sti_gdp_get_nb_formats, 567871bcdfeSVincent Abriou .prepare = sti_gdp_prepare, 568871bcdfeSVincent Abriou .commit = sti_gdp_commit, 569871bcdfeSVincent Abriou .disable = sti_gdp_disable, 570ba2d53fbSBenjamin Gaignard }; 571ba2d53fbSBenjamin Gaignard 572871bcdfeSVincent Abriou struct sti_plane *sti_gdp_create(struct device *dev, int desc, 573871bcdfeSVincent Abriou void __iomem *baseaddr) 574ba2d53fbSBenjamin Gaignard { 575ba2d53fbSBenjamin Gaignard struct sti_gdp *gdp; 576ba2d53fbSBenjamin Gaignard 577ba2d53fbSBenjamin Gaignard gdp = devm_kzalloc(dev, sizeof(*gdp), GFP_KERNEL); 578ba2d53fbSBenjamin Gaignard if (!gdp) { 579ba2d53fbSBenjamin Gaignard DRM_ERROR("Failed to allocate memory for GDP\n"); 580ba2d53fbSBenjamin Gaignard return NULL; 581ba2d53fbSBenjamin Gaignard } 582ba2d53fbSBenjamin Gaignard 583871bcdfeSVincent Abriou gdp->dev = dev; 584871bcdfeSVincent Abriou gdp->regs = baseaddr; 585871bcdfeSVincent Abriou gdp->plane.desc = desc; 586871bcdfeSVincent Abriou gdp->plane.ops = &gdp_plane_ops; 587871bcdfeSVincent Abriou 588ba2d53fbSBenjamin Gaignard gdp->vtg_field_nb.notifier_call = sti_gdp_field_cb; 589ba2d53fbSBenjamin Gaignard 590871bcdfeSVincent Abriou sti_gdp_init(gdp); 591871bcdfeSVincent Abriou 592871bcdfeSVincent Abriou return &gdp->plane; 593ba2d53fbSBenjamin Gaignard } 594