/****************************************************************************
 *  License : All rights reserved for TES Electronic Solutions GmbH
 *            See included /docs/license.txt for details
 *  Project : D/AVE HD
 *  Purpose : Textureunit (TXA/TXP) related inline functions
 ****************************************************************************
 * Version Control Information :
 *  $Revision: 4771 $
 *  $Date: 2015-02-10 12:01:30 +0100 (Di, 10. Feb 2015) $
 *  $LastChangedBy: florian.zimmermann $
 ****************************************************************************
 * Change History (autogenerated):
 ****************************************************************************/    

#ifndef DAVEHD_DRIVER_TXP_INL_H_INCLUDED
#define DAVEHD_DRIVER_TXP_INL_H_INCLUDED

/* PRQA S 3406 ++ *//* $Misra: #INLINE_FUNCTION_DEFINED_IN_HEADER $*/
/* PRQA S 3480 ++ *//* $Misra: #INLINE_FUNCTION_DEFINED_IN_HEADER $*/

/*----------------------------------------------------------------------------------------------------------*/
/* Register index (assumed to be for unit 0) is mapped to active unit and corresponding pending bit is set.*/
/**/
DHD_INLINE void dhd_internal_set_tex_busy(dhd_device_data_t *a_device, dhd_uint32_t a_register) {  /* PRQA S 3450 *//* $Misra: #INLINE_FUNCTION_NO_DECLARATION $*/
  dhd_uint32_t bit;

  /* all tex unit registers are group 10*/
  bit = (a_register - 10*64) + a_device->m_tex_unit_shift;                   
  a_device->m_pending_mask.m_groups.m_group10[ bit / 32 ] |= (1u << (bit % 32));
}


/*----------------------------------------------------------------------------------------------------------*/
/* Select the active texture unit. All following texture unit state changes refer to that texture unit. The */
/* available number of texture units can be queried using dhd_get_texture_unit_count. Selecting and configuring*/
/* a texture unit does not enable it. In order to enable units dhd_set_texture_unit_enable has to be used.*/
/* Selections of a nonexistant unit will be silently ignored.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_index : Index of texture unit to be selected (0..7)*/
/**/
/* See Also:*/
/*   dhd_get_texture_unit_count, dhd_set_texture_unit_enable*/
DHD_API_INLINE void dhd_active_texture_unit(dhd_handle_t a_device, dhd_uint32_t a_index) {
  dhd_device_data_t *device = dhd_get_device( a_device );

  if (a_index < device->m_description.m_num_tex_units) {
    /* store index of currently active texture unit*/
    device->m_tex_unit_index = (dhd_uint8_t)a_index;    
    device->m_tex_unit_shift = (dhd_uint8_t)(a_index * 7);
    device->m_tex_unit_base = ((dhd_uint32_t *) & device->m_shadow_state.m_named.m_tex_mode_0) + (a_index * 7);            
  }
}

/*----------------------------------------------------------------------------------------------------------*/
/* Get an active texture unit index. Retrieve currently active texture unit (see dhd_active_texture_unit for details). */
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/**/
/* Returns:*/
/*  Index of selected texture unit (0..7)*/
/**/
/* See Also:*/
/*  dhd_active_texture_unit*/
DHD_API_INLINE dhd_uint32_t dhd_get_active_texture_unit(dhd_handle_t a_device) {
  dhd_device_data_t *device = dhd_get_device( a_device );

  return device->m_tex_unit_index;
}

/*----------------------------------------------------------------------------------------------------------*/
/* Specify texture size. Sets the texture size and pitch for the active texture unit. Pitch*/
/* can be equal or greater than the width to enable the use of subrectangles within a larger buffer.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_width : Width in pixels*/
/*  a_height : Height in pixels*/
/*  a_pitch : Number of pixels in one line (equals width in a continuous buffer)*/
/**/
/* See Also:*/
/*   dhd_set_texture_address, dhd_set_texture_format*/
DHD_API_INLINE void dhd_set_texture_size(dhd_handle_t a_device, dhd_uint32_t a_width, dhd_uint32_t a_height, dhd_uint32_t a_pitch) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_uint32_t idx = device->m_tex_unit_index;

  /* get access to correct register slots (relative to group 6)*/
  dhd_reg_txa_size_0_t   *size  = (dhd_reg_txa_size_0_t *)  & device->m_shadow_state.m_groups.m_group6[(DHD_REG_TXA_SIZE_0  - (6*64)) + (idx * 2)];
  dhd_reg_txa_access_0_t *access = (dhd_reg_txa_access_0_t *) & device->m_shadow_state.m_groups.m_group6[(DHD_REG_TXA_ACCESS_0 - (6*64)) + (idx * 2)];
  dhd_reg_tex_mode_0_t   *mode  = (dhd_reg_tex_mode_0_t *) & device->m_tex_unit_base[ DHD_REG_TEX_MODE_0 - (10*64) ];

  /* store data*/
  size->m_fields.m_txa_width_0 = a_width;
  size->m_fields.m_txa_height_0 = a_height;
  access->m_fields.m_txa_pitch_0 = a_pitch; 

  /* update burst pitch if required (virtual tiling enabled)*/
  if (mode->m_fields.m_tex_virtual_tiling_mode_0 != 0) {
    dhd_reg_txp_ctrl_0_t *ctrl = (dhd_reg_txp_ctrl_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_CTRL_0 - (10*64) ];  
    const dhd_uint32_t texorg = mode->m_fields.m_tex_org_0;    
    dhd_uint32_t shift = 0; /* bpp = 1 for formats [8..]    */

    if (texorg < 2) {
      shift = 2; /* bpp = 4 for formats [0..1]*/
    } else {
      if (texorg < 8) {
        shift = 1; /* bpp = 2 for formats [2..7]*/
      }
    }
    ctrl->m_fields.m_txc_burst_pitch_0 = a_pitch << shift;    
    dhd_internal_set_tex_busy(device, DHD_REG_TXP_CTRL_0);
  }

  /* mark dirty bits (relative to group 6 start )*/
  device->m_pending_mask.m_groups.m_group6[ 0 ] |= 0x30u << (idx * 2);
}

/*----------------------------------------------------------------------------------------------------------*/
/* Get texture size. The texturesize and pitch as set by dhd_set_texture_size is written to the specified */
/* variables. Passing a null pointer indicates that a specific information is not requested.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_width :  Pointer to dhd_uint32_t that will receive the width in pixels (can be null)*/
/*  a_height : Pointer to dhd_uint32_t that will receive the height in pixels (can be null)*/
/*  a_pitch : Pointer to dhd_uint32_t that will receive the number of pixels per line (can be null)*/
/**/
/* See Also:*/
/*  dhd_set_texture_size*/
DHD_API_INLINE void dhd_get_texture_size(dhd_handle_t a_device, dhd_uint32_t *a_width, dhd_uint32_t *a_height, dhd_uint32_t *a_pitch) {  /* PRQA S 3673 *//* $Misra: #NECESSARY_FOR_API $*/
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_uint32_t idx = device->m_tex_unit_index;

  /* get access to correct register slots (relative to group 6)*/
  dhd_reg_txa_size_0_t   *size  = (dhd_reg_txa_size_0_t *)  & device->m_shadow_state.m_groups.m_group6[(DHD_REG_TXA_SIZE_0  - (6*64)) + (idx * 2)];
  dhd_reg_txa_access_0_t *access = (dhd_reg_txa_access_0_t *) & device->m_shadow_state.m_groups.m_group6[(DHD_REG_TXA_ACCESS_0 - (6*64)) + (idx * 2)];
  
  if (a_width != 0) {
    *a_width = size->m_fields.m_txa_width_0;
  }
  if (a_height != 0) {
    *a_height = size->m_fields.m_txa_height_0;
  }
  if (a_pitch != 0) {
    *a_pitch = access->m_fields.m_txa_pitch_0;
  }    
}

/* --------------------------------------------------------------------------------------------------------*/
/* Specify texture address. Sets the texture address for the active texture unit. The address must be in*/
/* GPU accessible memory and is usually obtained by a call to dhd_get_buffer_gpuptr. Memory layout and*/
/* pixel organization are specified separately using dhd_set_texture_size and dhd_set_texture_format.*/
/* Sub-byte texture formats (e.g. E_DHD_TEXEL_ORG_AL_4, E_DHD_TEXEL_ORG_AL_2,..) need an additional*/
/* bitoffset to specify the exact top left pixel.*/
/* */
/* */
/* */
/* \Note that setting the start address of the texture unaligned to the bus width of the actual D/AVE HW*/
/* instance is only allowed when using linear memory access (see dhd_set_texture_format).*/
/*                                                                                                       */
/* Parameters:*/
/*  a_device :     Driver context handle (as returned by dhd_init)*/
/*  a_address :    Address of the top left texel inside the texture*/
/*  a_bitoffset :  Number of pixels to skip from the position referenced by a_address*/
/*                                                                                                       */
/* See Also:*/
/*  dhd_set_texture_size, dhd_set_texture_format, dhd_flush_texture_cache                                  */
DHD_API_INLINE void dhd_set_texture_address(dhd_handle_t a_device, dhd_gpu_ptr_t a_address, dhd_uint8_t a_bitoffset) {  /* PRQA S 3673 *//* $Misra: #NECESSARY_FOR_API $*/
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_reg_tex_mode_0_t *mode = (dhd_reg_tex_mode_0_t *) &device->m_tex_unit_base[ DHD_REG_TEX_MODE_0 - (10*64) ];
  dhd_uint32_t offset = device->m_tex_unit_shift;

  /* store data (relative to group 10)*/
  device->m_shadow_state.m_groups.m_group10[(DHD_REG_TXC_START_ADDRESS_0 - (10*64)) + offset] = (dhd_uint32_t) (dhd_size_t) a_address;
  mode->m_fields.m_txa_address_bit_offset_0 = a_bitoffset;
  dhd_internal_set_tex_busy(device, DHD_REG_TEX_MODE_0);
  dhd_internal_set_tex_busy(device, DHD_REG_TXC_START_ADDRESS_0);
}

/*----------------------------------------------------------------------------------------------------------*/
/* Get texture address. Retrieve the current texture address and bitoffset (see dhd_set_texture_address*/
/* for details). Passing a null pointer indicates that a specific information is not requested. */
/* The most important information (GPU side texture address) is returned directly.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_bitoffset : Pointer to dhd_uint8_t that will receive the bit offset (can be null)*/
/**/
/* Returns:*/
/*  Address of the top left texel inside the texture*/
/* */
/* See Also:*/
/*  dhd_set_texture_address*/
DHD_API_INLINE dhd_gpu_ptr_t dhd_get_texture_address(dhd_handle_t a_device, dhd_uint8_t *a_bitoffset) {
  dhd_device_data_t *device = dhd_get_device( a_device );  
  dhd_uint32_t offset = device->m_tex_unit_shift;

  if (a_bitoffset != 0) {
    dhd_reg_tex_mode_0_t *mode = (dhd_reg_tex_mode_0_t *) &device->m_tex_unit_base[ DHD_REG_TEX_MODE_0 - (10*64) ];
    *a_bitoffset = (dhd_uint8_t) mode->m_fields.m_txa_address_bit_offset_0;
  }

  return (dhd_gpu_ptr_t) (dhd_size_t) device->m_shadow_state.m_groups.m_group10[(DHD_REG_TXC_START_ADDRESS_0 - (10*64)) + offset];
}

/*----------------------------------------------------------------------------------------------------------*/
/* Flush the current texture units cache. Ensures that the texture cache is flushed prior to the next read.*/
/* A flush is implicit on every change of the texture address (see dhd_set_texture_address). You need this  */
/* command only if the texture has been modified in memory and the address was not set again.*/
/* This command affects only the currently active texture unit (dhd_active_texture_unit).*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/**/
/* See Also:*/
/*  dhd_set_texture_address*/
DHD_API_INLINE void dhd_flush_texture_cache(dhd_handle_t a_device) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  
  dhd_internal_set_tex_busy(device, DHD_REG_TXC_START_ADDRESS_0);
}

/*----------------------------------------------------------------------------------------------------------*/
/* Set texture address mode. Selects the texture adressing for the active texture unit.*/
/* */
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_uaxis : Wrap mode for the texture U axis (see dhd_wrap_mode_t)*/
/*  a_vaxis : Wrap mode for the texture V axis (see dhd_wrap_mode_t)*/
DHD_API_INLINE void dhd_set_texture_wrap(dhd_handle_t a_device, dhd_wrap_mode_t a_uaxis, dhd_wrap_mode_t a_vaxis) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_reg_tex_mode_0_t *mode = (dhd_reg_tex_mode_0_t *) &device->m_tex_unit_base[ DHD_REG_TEX_MODE_0 - (10*64) ];

  mode->m_fields.m_txa_wrap_mode_u_0 = a_uaxis;
  mode->m_fields.m_txa_wrap_mode_v_0 = a_vaxis;
  dhd_internal_set_tex_busy(device, DHD_REG_TEX_MODE_0);
}

/*----------------------------------------------------------------------------------------------------------*/
/* Get texture address mode. Retrieve the currently active texture adressing (see dhd_set_texture_wrap*/
/* for details). Passing a null pointer indicates that a specific information is not requested.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_uaxis : Pointer to dhd_wrap_mode_t that will receive the texture U axis wrap mode (can be null)*/
/*  a_vaxis : Pointer to dhd_wrap_mode_t that will receive the texture V axis wrap mode (can be null)*/
/* */
/* See Also:*/
/*  dhd_set_texture_wrap*/
DHD_API_INLINE void dhd_get_texture_wrap(dhd_handle_t a_device, dhd_wrap_mode_t *a_uaxis, dhd_wrap_mode_t *a_vaxis) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_reg_tex_mode_0_t *mode = (dhd_reg_tex_mode_0_t *) &device->m_tex_unit_base[ DHD_REG_TEX_MODE_0 - (10*64) ];

  if (a_uaxis != 0) {
    *a_uaxis = (dhd_wrap_mode_t) mode->m_fields.m_txa_wrap_mode_u_0;
  }
  if (a_vaxis != 0) {
    *a_vaxis = (dhd_wrap_mode_t) mode->m_fields.m_txa_wrap_mode_v_0;
  }
}

/*----------------------------------------------------------------------------------------------------------*/
/* Enable texture filter mode. Selects the texture filtering for the active texture unit.*/
/* Note that dhd_set_texture_filter and dhd_set_texture_kernel are mutually exclusive.*/
/* */
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_uaxis : Boolean flag. 0 to disable filtering on U axis.*/
/*  a_vaxis : Boolean flag. 0 to disable filtering on V axis.*/
/**/
/* See Also:*/
/*  dhd_set_texture_kernel*/
DHD_API_INLINE void dhd_set_texture_filter(dhd_handle_t a_device, dhd_bool_t a_uaxis, dhd_bool_t a_vaxis) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_uint32_t idx = device->m_tex_unit_index;
  dhd_reg_tex_mode_0_t *mode = (dhd_reg_tex_mode_0_t *) & device->m_tex_unit_base[ DHD_REG_TEX_MODE_0 - (10*64) ];
  dhd_reg_txa_access_0_t *access = (dhd_reg_txa_access_0_t *) & device->m_shadow_state.m_groups.m_group6[(DHD_REG_TXA_ACCESS_0 - (6*64)) + (idx * 2)];

  mode->m_fields.m_tex_filter_u_0 = a_uaxis;
  mode->m_fields.m_tex_filter_v_0 = a_vaxis;
    
  access->m_fields.m_txa_filter_kernel_width_0  = 2 - 1;
  access->m_fields.m_txa_filter_kernel_height_0 = 2 - 1;
  access->m_fields.m_txa_filter_sample_shape_0  = 0; /* E_FILTER_SAMPLE_SHAPE_QUAD*/
  
  dhd_internal_set_tex_busy(device, DHD_REG_TEX_MODE_0);
  /* mark dirty bits (relative to group 6 start )*/
  device->m_pending_mask.m_groups.m_group6[ 0 ] |= 0x20u << (idx * 2);  
}

/*----------------------------------------------------------------------------------------------------------*/
/* Get texture filter mode. Returns the current state of the bilinear filter (see dhd_set_texture_filter*/
/* for details). Note that when convolution based filtering (see dhd_set_texture_kernel) is enabled both*/
/* axis bilinear filters are enabled as well. */
/* Passing a null pointer indicates that a specific information is not requested.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_uaxis : Pointer to dhd_bool_t that will be set to DHD_TRUE when U axis filtering is enabled (can be null)*/
/*  a_vaxis : Pointer to dhd_bool_t that will be set to DHD_TRUE when V axis filtering is enabled (can be null)*/
/* */
/* See Also:*/
/*  dhd_set_texture_filter*/
DHD_API_INLINE void dhd_get_texture_filter(dhd_handle_t a_device, dhd_bool_t *a_uaxis, dhd_bool_t *a_vaxis) {
  dhd_device_data_t *device = dhd_get_device( a_device );  
  dhd_reg_tex_mode_0_t *mode = (dhd_reg_tex_mode_0_t *) & device->m_tex_unit_base[ DHD_REG_TEX_MODE_0 - (10*64) ];

  if (a_uaxis != 0) {
    *a_uaxis = mode->m_fields.m_tex_filter_u_0;
  }
  if (a_vaxis != 0) {
    *a_vaxis = mode->m_fields.m_tex_filter_v_0;
  }
}

/* --------------------------------------------------------------------------------------------------------*/
/* Specify texture format. The texture format describes pixel organization (bits per channel and channel*/
/* order) as well as channel counts (alpha and color, color only, alpha only, luminance and alpha..) In*/
/* addition to the texel organisation the memory layout can also be specified (linear, tiled,*/
/* swizzled..)*/
/* */
/* For complex use cases the texel organisation can be combined with a CLUT mode (see*/
/* dhd_clutmode_org_t) e.g. 'E_DHD_TEXEL_ORG_RGB_565 | E_DHD_CLUT_MAP_RGB' to use rgb565 input with each*/
/* channel going through the LUT. Note that this does not work for the E_DHD_TEXEL_ORG_CLUT_* modes as*/
/* those are already combinations of a regular texel org and E_DHD_CLUT_INDEXED_COLOR.*/
/* */
/* */
/* */
/* The following restrictions regarding texture start address and pitch/height apply:*/
/*   - The texture start address must only be unaligned to the bus width of the D/AVE HD HW instance*/
/*     when using normal linear access.*/
/*   - In case of block/tiled modes (swizzle 4/16 and virtual tiled), the pitch/height must be a*/
/*     multiple of the tile width/height.*/
/*   - In case of full swizzle, pitch and height must the same power&#45;of&#45;2 (= square).*/
/*                                                                                                       */
/* Parameters:*/
/*  a_device :         Driver context handle (as returned by dhd_init)*/
/*  a_format :         Texture format (combination of dhd_texel_org_t and dhd_clutmode_org_t)*/
/*  a_address :        Texture memory access (see dhd_address_mode_t)*/
/*  a_discard_alpha :  Boolean flag. 0 to keep the texture alpha. 1 to replace it with a constant 255*/
/*                     (opaque)*/
/*                                                                                                       */
/* See Also:*/
/*  dhd_set_texture_size, dhd_set_texture_address                                                          */
DHD_API_INLINE void dhd_set_texture_format(dhd_handle_t a_device, dhd_texel_org_t a_format, dhd_address_mode_t a_address, dhd_bool_t a_discard_alpha) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_reg_tex_mode_0_t *mode = (dhd_reg_tex_mode_0_t *) &device->m_tex_unit_base[ DHD_REG_TEX_MODE_0 - (10*64) ];
  dhd_reg_txp_ctrl_0_t *ctrl = (dhd_reg_txp_ctrl_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_CTRL_0 - (10*64) ];  
  dhd_uint32_t limit = (dhd_uint32_t)((a_format >> 8) & 1);

  if (a_discard_alpha != 0) {
    limit = 0;    /* we must prevent limit_rgb to be set in combination with discard_alpha (HW result is undefined and it doesn't make sense either)*/
  }

  mode->m_fields.m_txa_swizzle_mode_0         = a_address & 0x7;
  mode->m_fields.m_tex_virtual_tiling_mode_0  = a_address >> 3;
  mode->m_fields.m_txp_discard_alpha_0        = a_discard_alpha;
  mode->m_fields.m_tex_org_0                  = a_format & 0xf;
  mode->m_fields.m_txp_limit_color_to_alpha_0 = limit;
  ctrl->m_fields.m_txp_clut_mode_0            = (a_format >> 4) & 0xf;  

  /* update burst pitch if required (virtual tiling enabled)*/
  if ((a_address >> 3) != 0) {
    const dhd_uint32_t texorg = (dhd_uint32_t)a_format & 0xfu;    
    dhd_uint32_t shift = 0; /* bpp = 1 for formats [8..]    */
    dhd_uint32_t idx = device->m_tex_unit_index;
    dhd_reg_txa_access_0_t *access = (dhd_reg_txa_access_0_t *) & device->m_shadow_state.m_groups.m_group6[(DHD_REG_TXA_ACCESS_0 - (6*64)) + (idx * 2)];

    if (texorg < 2) {
      shift = 2; /* bpp = 4 for formats [0..1]*/
    } else {
      if (texorg < 8) {
        shift = 1; /* bpp = 2 for formats [2..7]*/
      }
    }
    ctrl->m_fields.m_txc_burst_pitch_0 = access->m_fields.m_txa_pitch_0 << shift;    
  }
  
  dhd_internal_set_tex_busy(device, DHD_REG_TEX_MODE_0);
  dhd_internal_set_tex_busy(device, DHD_REG_TXP_CTRL_0);
}

/*----------------------------------------------------------------------------------------------------------*/
/* Get texture format. Retrieve the currently set texel organisation and memory layout (see */
/* dhd_set_texture_format for details). Passing a null pointer indicates that a specific information is not */
/* requested. The most important information (texel organisation) is returned directly.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_address : Pointer to dhd_address_mode_t that will receive the texture memory access (can be null)*/
/*  a_discard_alpha : Pointer to dhd_bool_t that will receive the discard alpha flag (can be null)*/
/**/
/* Returns:*/
/*  Texture format (dhd_texel_org_t)*/
/* */
/* See Also:*/
/*  dhd_set_texture_format*/
DHD_API_INLINE dhd_texel_org_t dhd_get_texture_format(dhd_handle_t a_device, dhd_address_mode_t *a_address, dhd_bool_t *a_discard_alpha) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_reg_tex_mode_0_t *mode = (dhd_reg_tex_mode_0_t *) &device->m_tex_unit_base[ DHD_REG_TEX_MODE_0 - (10*64) ];
  dhd_reg_txp_ctrl_0_t *ctrl = (dhd_reg_txp_ctrl_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_CTRL_0 - (10*64) ];  
  dhd_uint32_t texelorg;

  texelorg = mode->m_fields.m_tex_org_0 |        
             (ctrl->m_fields.m_txp_clut_mode_0 << 4);
  if (mode->m_fields.m_txp_limit_color_to_alpha_0 != 0) {
    texelorg |= DHD_PREMULTIPLY_LIMIT_RGB;
  }

  if (a_address != 0) {
    *a_address = (dhd_address_mode_t) ( mode->m_fields.m_txa_swizzle_mode_0 | (mode->m_fields.m_tex_virtual_tiling_mode_0 << 3) );
  }
  if (a_discard_alpha != 0) {
    *a_discard_alpha = mode->m_fields.m_txp_discard_alpha_0;
  }

  return (dhd_texel_org_t) (texelorg | DHD_TEXEL_ORG_FLAG);
}

/*----------------------------------------------------------------------------------------------------------*/
/* Enable and specify color key. Color keying turns texels transparent that match a certain reference color.*/
/* The function sets color key parameters for the active texture unit.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_enable : Boolean flag. 0 to disable color keying (a_color is ignored in this case)*/
/*  a_color : Key color in ARGB8888 format*/
DHD_API_INLINE void dhd_set_texture_color_key(dhd_handle_t a_device, dhd_uint32_t a_color, dhd_bool_t a_enable) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_reg_txp_ctrl_0_t *ctrl = (dhd_reg_txp_ctrl_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_CTRL_0 - (10*64) ];

  ctrl->m_fields.m_txp_color_key_enable_0 = a_enable;
  dhd_internal_set_tex_busy(device, DHD_REG_TXP_CTRL_0);
  if (a_enable != 0) {
    /* no need to set the color if keying is turned off*/
    dhd_reg_txp_color_key_0_t *key = (dhd_reg_txp_color_key_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_COLOR_KEY_0 - (10*64) ];
    key->m_fields.m_txp_color_key_0 = a_color & 0x00ffffffu;
    dhd_internal_set_tex_busy(device, DHD_REG_TXP_COLOR_KEY_0);
  }
}

/*----------------------------------------------------------------------------------------------------------*/
/* Get color key. Retrieve the currently set color key parameters (see dhd_set_texture_color_key).*/
/* Passing a null pointer indicates that a specific information is not requested. */
/* The most important information (key color) is returned directly.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_enable : Pointer to dhd_bool_t that will be set to DHD_TRUE when color keying is enabled (can be null)*/
/**/
/* Returns:*/
/*  Key color in ARGB8888 format (note that alpha is always 0)*/
/* */
/* See Also:*/
/*  dhd_set_texture_color_key*/
DHD_API_INLINE dhd_uint32_t dhd_get_texture_color_key(dhd_handle_t a_device, dhd_bool_t *a_enable) {
  dhd_device_data_t *device = dhd_get_device( a_device );  
  dhd_reg_txp_color_key_0_t *key = (dhd_reg_txp_color_key_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_COLOR_KEY_0 - (10*64) ];

  if (a_enable != 0) {
    dhd_reg_txp_ctrl_0_t *ctrl = (dhd_reg_txp_ctrl_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_CTRL_0 - (10*64) ];
    *a_enable = ctrl->m_fields.m_txp_color_key_enable_0;
  }

  return key->m_fields.m_txp_color_key_0;
}

/*----------------------------------------------------------------------------------------------------------*/
/* Specify border color. The border color is used when a texture wrap mode of E_DHD_TEX_BORDER is active (see */
/* dhd_set_texture_wrap) and a texel outside of the texture is addressed.*/
/**/
/* Note: When the corresponding texture unit is using indexed color(E_DHD_TEXEL_ORG_CLUT_* or E_DHD_CLUT_INDEXED_COLOR)*/
/* the a_color parameter is mapped through the clut as well.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_color : Border color in ARGB8888 format or CLUT index*/
/**/
DHD_API_INLINE void dhd_set_texture_border_color(dhd_handle_t a_device, dhd_uint32_t a_color) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_reg_txp_fill_color_0_t *fill = (dhd_reg_txp_fill_color_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_FILL_COLOR_0 - (10*64) ];

  fill->m_fields.m_txp_fill_color_0 = a_color;
  dhd_internal_set_tex_busy(device, DHD_REG_TXP_FILL_COLOR_0);
}

/*----------------------------------------------------------------------------------------------------------*/
/* Get border color. Retrieve currently active texture border color (see dhd_set_texture_border_color for details). */
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/**/
/* Returns:*/
/*  Border color in ARGB8888 format or CLUT index*/
/**/
/* See Also:*/
/*  dhd_set_texture_border_color*/
DHD_API_INLINE dhd_uint32_t dhd_get_texture_border_color(dhd_handle_t a_device) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_reg_txp_fill_color_0_t *fill = (dhd_reg_txp_fill_color_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_FILL_COLOR_0 - (10*64) ];

  return fill->m_fields.m_txp_fill_color_0;
}

/*----------------------------------------------------------------------------------------------------------*/
/* Specify a CLUT offset. All clut texture indices are relative to the clut offset. As the CLUT is a shared*/
/* resource the offset is usually based on the allocated CLUT slice (see dhd_alloc_clut).*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_offset : CLUT offset*/
DHD_API_INLINE void dhd_set_texture_clut_base(dhd_handle_t a_device, dhd_uint32_t a_offset) {
  dhd_device_data_t *device = dhd_get_device( a_device );

  if (a_offset < device->m_description.m_num_clut_entries) {
    dhd_reg_txp_clut_offset_0_t *clut = (dhd_reg_txp_clut_offset_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_CLUT_OFFSET_0 - (10*64) ];
    clut->m_fields.m_txp_clut_offset_0 = a_offset;
    dhd_internal_set_tex_busy(device, DHD_REG_TXP_CLUT_OFFSET_0);
  }
}

/*----------------------------------------------------------------------------------------------------------*/
/* Get CLUT offset. Retrieve currently active texture border color (see dhd_set_texture_clut_base for details). */
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/**/
/* Returns:*/
/*  CLUT offset*/
/**/
/* See Also:*/
/*  dhd_set_texture_clut_base*/
DHD_API_INLINE dhd_uint32_t dhd_get_texture_clut_base(dhd_handle_t a_device) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_reg_txp_clut_offset_0_t *clut = (dhd_reg_txp_clut_offset_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_CLUT_OFFSET_0 - (10*64) ];

  return clut->m_fields.m_txp_clut_offset_0;
}

/* --------------------------------------------------------------------------------------------------------*/
/* Upload a convolution kernel. The kernel weights will be paced in the CLUT at the specified offset. A*/
/* separable kernel will require 'number of weights' divided by four entires (rounded up) in the CLUT,*/
/* while a non separable (both dimensions \> 1) might require more space as both dimensions are first*/
/* rounded up to the next even number. So a e.g. 3x1 kernel will use 1 CLUT entry and a 3x3 kernel will*/
/* use 4 CLUT entries.*/
/* */
/* Textures that should use the convolution kernel have to specify E_DHD_CLUT_KERNEL or*/
/* E_DHD_CLUT_KERNEL_SIGNED flags as part of their format (see dhd_set_texture_format and*/
/* dhd_clutmode_org_t).*/
/* */
/* */
/* The following restrictions regarding the kernel size apply :*/
/*   - The texture adress mode has to be E_DHD_ACCESS_VIRTUAL_TILED_4 or higher when the kernel width is 1.*/
/*   - In wrap modes E_DHD_TEX_CLAMP or E_DHD_TEX_BORDER kernel size must not exceed the texture size.*/
/*   - Texture must have at least 4 texels (e.g 2x2, 1x4, 4x1 or larger).*/
/* */
/* \Note that dhd_set_texture_kernel and dhd_set_texture_filter are mutually exclusive. After using*/
/* kernel based convolution use dhd_set_texture_filter to switch back to standard bilinear filtering.*/
/*                                                                                                       */
/* Parameters:*/
/*  a_device :    Driver context handle (as returned by dhd_init)*/
/*  a_width :     Kernel width (between 1 and 16)*/
/*  a_height :    Kernel height (between 1 and 16)*/
/*  a_data :      Pointer to 8bit kernel weights. (pointer is unsigned but weights can be signed)*/
/*  a_clutbase :  Offset in the CLUT (as allocated by dhd_alloc_clut)*/
/*                                                                                                       */
/* See Also:*/
/*  dhd_set_texture_format, dhd_alloc_clut, dhd_set_texture_scalebias, dhd_set_texture_filter              */
DHD_API_INLINE void dhd_set_texture_kernel(dhd_handle_t a_device, dhd_uint32_t a_width, dhd_uint32_t a_height, const dhd_uint8_t *a_data, dhd_uint32_t a_clutbase) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_uint32_t idx = device->m_tex_unit_index;
  dhd_uint32_t i,x,y,shape,cnt;
  dhd_uint32_t clut[64];  
  const dhd_uint8_t *src = a_data;
  dhd_reg_txa_access_0_t *access = (dhd_reg_txa_access_0_t *) & device->m_shadow_state.m_groups.m_group6[(DHD_REG_TXA_ACCESS_0 - (6*64)) + (idx * 2)];
  dhd_reg_tex_mode_0_t *mode = (dhd_reg_tex_mode_0_t *) & device->m_tex_unit_base[ DHD_REG_TEX_MODE_0 - (10*64) ];

  if ((a_width > 16) || (a_height > 16) || (a_width < 1) || (a_height < 1)) {
    /* illegal kernel size*/
    return;
  }

  if ((a_height == 1) || (a_width == 1)) {
    cnt = ((a_width * a_height)+3) >> 2;              /* number of quad subsamples */
    /* clear clut section we are going to use*/
    for (i=0; i<cnt; i++) {        
      clut[i] = 0;        
    }
    /* separable kernel*/
    for (i=0; i<(a_width * a_height); i++) {                    
      dhd_uint32_t qi = i >> 2;                       /* quad index for current weight*/
      dhd_uint32_t qp = i & 3u;                       /* quad position (color channel) for current weight*/
      clut[qi] |= (dhd_uint32_t)((*src) << ((qp ^ 3u)*8u));           /* put weight into destination channel*/
      src++;
    }
    if (a_height == 1) {
      shape = 1; /* E_FILTER_SAMPLE_SHAPE_ROW*/
    } else {    
      shape = 2; /* E_FILTER_SAMPLE_SHAPE_COLUMN*/
    }
  } else {        
    dhd_uint32_t w = (a_width + 1) >> 1;              /* number of quad subsamples per line*/
    dhd_uint32_t h = (a_height + 1) >> 1;             /* number of quad subsamples per column    */
    /* clear clut section we are going to use*/
    cnt = w*h;
    for (i=0; i<cnt; i++) {        
      clut[i] = 0;        
    }
    /* non separable kernel    */
    for (y=0; y<a_height; y++) {        
      for (x=0; x<a_width; x++) {                    
        dhd_uint32_t qi = (x >> 1) + ((y >> 1)*w);      /* quad index for current weight*/
        dhd_uint32_t qp = (x & 1) + ((y & 1u) * 2u);    /* quad position (color channel) for current weight*/
        clut[qi] |= (dhd_uint32_t)((*src) << ((qp ^ 3u)*8u));         /* put weight into destination channel*/
        src++;
      }
    }      
    shape = 0; /* E_FILTER_SAMPLE_SHAPE_QUAD    */
  }

  /* upload clut*/
  (void)dhd_set_clut(a_device, clut, a_clutbase, cnt);
  /* setup registers*/
  dhd_set_texture_clut_base(a_device, a_clutbase);

  access->m_fields.m_txa_filter_sample_shape_0  = shape;
  access->m_fields.m_txa_filter_kernel_width_0 = a_width - 1;
  access->m_fields.m_txa_filter_kernel_height_0 = a_height - 1;  
  mode->m_fields.m_tex_filter_u_0 = 1;
  mode->m_fields.m_tex_filter_v_0 = 1;
  /* mark dirty bits (relative to group 6 start )*/
  device->m_pending_mask.m_groups.m_group6[ 0 ] |= 0x20u << (idx * 2);  
  dhd_internal_set_tex_busy(device, DHD_REG_TEX_MODE_0);
}

/*----------------------------------------------------------------------------------------------------------*/
/* Set texture scale and bias. Scale and bias are applied after a texel has been filtered. They are normally*/
/* used in combination with dhd_set_texture_kernel to apply a convolution kernel, but it can be used on any*/
/* texture unit.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_scale : Floating point scale to be applied (has to be positive)*/
/*  a_bias : Integer bias between -265 and 255 (applied after the scale)*/
/**/
DHD_API_INLINE void dhd_set_texture_scalebias(dhd_handle_t a_device, dhd_float32_t a_scale, dhd_int32_t a_bias ) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_reg_txp_filter_scale_bias_0_t *scale = (dhd_reg_txp_filter_scale_bias_0_t *) &device->m_tex_unit_base[ DHD_REG_TXP_FILTER_SCALE_BIAS_0 - (10*64) ];
  union fltconvert_t {
    dhd_float32_t m_float;  /* PRQA S 3629 *//* $Misra: Union containing float is used locally with defined floating-point storage layout. $*/
    struct fltbits_t {        
      dhd_uint32_t  m_mantissa : 23;
      dhd_uint32_t  m_exponent : 8;
      dhd_uint32_t  m_sign : 1;    
    } m_bits;    
  } fltconvert;
  dhd_int32_t exponent,sig;

  fltconvert.m_float = a_scale;        
  exponent = 9 - (fltconvert.m_bits.m_exponent - 127);     /* exponent with new bias and flipped sign*/
  sig = (fltconvert.m_bits.m_mantissa >> 14) | 0x200; /* 10bit significant (including implicit '1')*/
  if (exponent > 31) {
    /* value too small, clamp to 0 (applies to all input denormals as well)*/
    sig = 0;
    exponent = 0;
  } else {  
    if (exponent < -32) {
      /* value too big, clamp to maximum (applies to NANs and INFs as well)*/
      exponent = -32;
      sig = 1023;
    }    
  }
  
  scale->m_fields.m_txp_filter_scale_0 = sig;
  scale->m_fields.m_txp_filter_shift_0 = exponent;
  scale->m_fields.m_txp_filter_bias_0  = a_bias;  
  dhd_internal_set_tex_busy(device, DHD_REG_TXP_FILTER_SCALE_BIAS_0);
}

/*----------------------------------------------------------------------------------------------------------*/
/* Enable texture units. All texture units from 0 to a_count-1 are enabled all others disabled.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_count : Number of texture units to enable (0..8)*/
/**/
/* See Also:*/
/*  dhd_get_texture_unit_count*/
DHD_API_INLINE void dhd_set_texture_unit_enable(dhd_handle_t a_device, dhd_uint32_t a_count) {
  dhd_device_data_t *device = dhd_get_device( a_device );

  if (a_count > device->m_description.m_num_tex_units) {
    a_count = device->m_description.m_num_tex_units;
  }
  
  device->m_shadow_state.m_named.m_tex_global.m_fields.m_tex_num_units = a_count;

  /**
   * Issue 0000428: ZSS_Span_Length_Limit register potentially needs to be changed to avoid HW hangup when more than one texture unit is enabled
   * Divide FIFO depth by number of enabled texure units.
   */
  device->m_shadow_state.m_named.m_zss_span_config.m_fields.m_zss_span_length_limit = (a_count > 0) ? ((device->m_description.m_zsaread_fifo_depth / a_count)-1) : (device->m_description.m_zsaread_fifo_depth-1);

  device->m_pending_mask.m_named.m_tex_global = 1;
  device->m_pending_mask.m_named.m_zss_span_config = 1;
}

/*----------------------------------------------------------------------------------------------------------*/
/* Get number of active texture units. Returns the value last set by dhd_set_texture_unit_enable.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/**/
/* Returns:*/
/*  Number of enabled texture units.*/
/**/
/* See Also:*/
/*  dhd_set_texture_unit_enable*/
DHD_API_INLINE dhd_uint32_t dhd_get_texture_unit_enable(dhd_handle_t a_device) {
  dhd_device_data_t *device = dhd_get_device( a_device );

  return device->m_shadow_state.m_named.m_tex_global.m_fields.m_tex_num_units;
}

/*----------------------------------------------------------------------------------------------------------*/
/* Setup RLE decoder. RLE compressed textures can be decompressed on the fly. As there is only a single */
/* hardware instance of the decoder it can only be associated with one texture unit. Therefore the function*/
/* does not depend on the active unit but receives its own a_unit parameter.*/
/* In order to disable RLE decoding an address of 0 has to be specified.*/
/**/
/* Note: */
/*  RLE decoding implicitly replaces the address specified using dhd_set_texture_address with a_address.*/
/*  The RLE texture start address has to be aligned to the bus width.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init)*/
/*  a_unit : Texture unit that has or had an RLE texture bound*/
/*  a_address : Address of the first byte of the RLE encoded texture or 0 to disable the RLE decoder*/
/*  a_wordsize : Number of bytes per pixel */
/*  */
/* Returns:*/
/*   E_DHD_OK when the operation succeeded or*/
/*   E_DHD_ERROR_INVALID_PTR in case of a misaligned RLE texture or*/
/*   E_DHD_ERROR_INVALID_ARGUMENT in case of an invalid device handle or wordsize.*/
/**/
DHD_API_INLINE dhd_enum_t dhd_set_texture_rle_decoder(dhd_handle_t a_device, dhd_uint32_t a_unit, dhd_gpu_ptr_t a_address, dhd_uint32_t a_wordsize) { /* PRQA S 3673 *//* $Misra: #NECESSARY_FOR_API $*/
  dhd_device_data_t *device = (dhd_device_data_t *) a_device;
  dhd_uint32_t i, enable, bit;
  dhd_enum_t result = E_DHD_OK;

  /* enable only if address is not null*/
  if (a_address != 0) {
    enable = 1;
  } else {
    enable = 0;
  }

  /* validate arguments*/
  if ( (dhd_validate_device(device) != E_DHD_OK) || ((a_wordsize == 0) && (enable != 0)) ) {
    return E_DHD_ERROR_INVALID_ARGUMENT;
  }

  if (((dhd_size_t)a_address & ((dhd_size_t)device->m_description.m_bus_width - 1)) != 0) {
    return E_DHD_ERROR_INVALID_PTR;
  }

  /* set rld control bits for *all* texture units*/
  for (i=0; i<device->m_description.m_num_tex_units; i++) {
    dhd_bool_t changed = 0;
    dhd_uint32_t ctrlindex = (DHD_REG_TXP_CTRL_0 - (10*64)) + (i*7);
    dhd_reg_txp_ctrl_0_t *ctrl = (dhd_reg_txp_ctrl_0_t *) & device->m_shadow_state.m_groups.m_group10[ ctrlindex ];
    if (a_unit == i) {      
      if (ctrl->m_fields.m_txc_use_rld_0 != enable) {
        changed = 1;
      }
      ctrl->m_fields.m_txc_use_rld_0 = enable;
    } else {
      if (ctrl->m_fields.m_txc_use_rld_0 != 0) {
        changed = 1;
      }
      ctrl->m_fields.m_txc_use_rld_0 = 0;
    }

    if (changed != 0) {
      device->m_pending_mask.m_groups.m_group10[ ctrlindex / 32 ] |= (1u << (ctrlindex % 32));
    }
  }
  
  /* set details if enabled*/
  if (enable != 0) {
    device->m_shadow_state.m_named.m_rld_start_address.m_value = (dhd_uint32_t) (dhd_size_t) a_address;
    device->m_shadow_state.m_named.m_tex_global.m_fields.m_rld_pixel_width = a_wordsize - 1; /* PRQA S 3382 *//* $Misra: Misra checker fails to recognize variable will never overflow $*/
    device->m_pending_mask.m_named.m_rld_start_address = 1;
    device->m_pending_mask.m_named.m_tex_global = 1;
  }

  /* write texture address (just to ensure the HW flushes it's texture cache, it is not really using the value)*/
  device->m_shadow_state.m_groups.m_group10[(DHD_REG_TXC_START_ADDRESS_0 - (10*64)) + (a_unit*7)] = (dhd_uint32_t) (dhd_size_t) a_address;   
  bit = (DHD_REG_TXC_START_ADDRESS_0 - (10*64)) + (a_unit*7);                
  device->m_pending_mask.m_groups.m_group10[ bit / 32 ] |= (1u << (bit % 32));
  return result;
}

/*----------------------------------------------------------------------------------------------------------*/
/* Get the configured RLE decoding texture unit. When RLE decoding is activated for a texture unit, this*/
/* function will return the number of the texture unit, the configured address of the RLE encoded texture*/
/* and the wordsize. The parameters may be zero when they are not needed.*/
/* When RLE decoding is disabled, an address of 0 will be returned.*/
/**/
/* Parameters:*/
/*  a_device : Driver context handle (as returned by dhd_init).*/
/*  a_unit : Pointer to dhd_uint32_t that will be set to the texture unit that has the RLD bound.*/
/*  a_wordsize : Pointer to dhd_uint32_t that will be set to the number of bytes per pixel.*/
/**/
/* Returns: Address of the RLE texture*/
/**/
/* See Also: dhd_set_texture_rle_decoder*/
DHD_API_INLINE dhd_gpu_ptr_t dhd_get_texture_rle_decoder(dhd_handle_t a_device, dhd_uint32_t *a_unit, dhd_uint32_t *a_wordsize) {
  dhd_device_data_t *device = dhd_get_device( a_device );
  dhd_uint32_t i, enable = 0;
  dhd_gpu_ptr_t address;

  /* look for enabled unit*/
  for (i=0; i<device->m_description.m_num_tex_units; i++) {  
    dhd_uint32_t ctrlindex = (DHD_REG_TXP_CTRL_0 - (10*64)) + (i*7);
    dhd_reg_txp_ctrl_0_t *ctrl = (dhd_reg_txp_ctrl_0_t *) & device->m_shadow_state.m_groups.m_group10[ ctrlindex ];

    if (ctrl->m_fields.m_txc_use_rld_0 != 0) {
      enable = 1;
      break;
    }
  }  
  if (enable == 0) {
    /* RLE disabled, returning fixed 0*/
    address = (dhd_gpu_ptr_t) 0;
    i = 0;
  } else {
    address = (dhd_gpu_ptr_t) (dhd_size_t) device->m_shadow_state.m_named.m_rld_start_address.m_value;
  }

  if (a_unit != 0) {
    *a_unit = i;
  }
  if (a_wordsize != 0) {
    *a_wordsize = device->m_shadow_state.m_named.m_tex_global.m_fields.m_rld_pixel_width + 1;
  }

  return address;
}

/*----------------------------------------------------------------------------------------------------------*/

#endif /*DAVEHD_DRIVER_TXP_INL_H_INCLUDED*/
