1209ff23fSmrg#ifdef HAVE_CONFIG_H 2209ff23fSmrg#include "config.h" 3209ff23fSmrg#endif 4209ff23fSmrg 5209ff23fSmrg#include <string.h> 6209ff23fSmrg 7209ff23fSmrg#include "radeon.h" 8209ff23fSmrg#include "radeon_reg.h" 9209ff23fSmrg#include "radeon_macros.h" 10209ff23fSmrg#include "radeon_probe.h" 11209ff23fSmrg#include <X11/extensions/Xv.h> 12209ff23fSmrg#include "radeon_video.h" 13209ff23fSmrg 14209ff23fSmrg#include "xf86.h" 15209ff23fSmrg#include "atipciids.h" 16209ff23fSmrg 17209ff23fSmrg#include "generic_bus.h" 18209ff23fSmrg#include "theatre_reg.h" 19209ff23fSmrg 20209ff23fSmrg#define VIP_NAME "RADEON VIP BUS" 21209ff23fSmrg#define VIP_TYPE "ATI VIP BUS" 22209ff23fSmrg 23209ff23fSmrg/* Status defines */ 24209ff23fSmrg#define VIP_BUSY 0 25209ff23fSmrg#define VIP_IDLE 1 26209ff23fSmrg#define VIP_RESET 2 27209ff23fSmrg 28209ff23fSmrgstatic Bool RADEONVIP_ioctl(GENERIC_BUS_Ptr b, long ioctl, long arg1, char *arg2) 29209ff23fSmrg{ 30209ff23fSmrg long count; 31209ff23fSmrg switch(ioctl){ 32209ff23fSmrg case GB_IOCTL_GET_NAME: 33209ff23fSmrg count=strlen(VIP_NAME)+1; 34209ff23fSmrg if(count>arg1)return FALSE; 35209ff23fSmrg memcpy(arg2,VIP_NAME,count); 36209ff23fSmrg return TRUE; 37209ff23fSmrg 38209ff23fSmrg case GB_IOCTL_GET_TYPE: 39209ff23fSmrg count=strlen(VIP_TYPE)+1; 40209ff23fSmrg if(count>arg1)return FALSE; 41209ff23fSmrg memcpy(arg2,VIP_TYPE,count); 42209ff23fSmrg return TRUE; 43209ff23fSmrg 44209ff23fSmrg default: 45209ff23fSmrg return FALSE; 46209ff23fSmrg } 47209ff23fSmrg} 48209ff23fSmrg 49209ff23fSmrgstatic uint32_t RADEONVIP_idle(GENERIC_BUS_Ptr b) 50209ff23fSmrg{ 5168105dcbSveego ScrnInfoPtr pScrn = b->pScrn; 52209ff23fSmrg RADEONInfoPtr info = RADEONPTR(pScrn); 53209ff23fSmrg unsigned char *RADEONMMIO = info->MMIO; 54209ff23fSmrg 55209ff23fSmrg uint32_t timeout; 56209ff23fSmrg 57209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 58209ff23fSmrg timeout = INREG(RADEON_VIPH_TIMEOUT_STAT); 59209ff23fSmrg if(timeout & RADEON_VIPH_TIMEOUT_STAT__VIPH_REG_STAT) /* lockup ?? */ 60209ff23fSmrg { 61209ff23fSmrg RADEONWaitForFifo(pScrn, 2); 62209ff23fSmrg OUTREG(RADEON_VIPH_TIMEOUT_STAT, (timeout & 0xffffff00) | RADEON_VIPH_TIMEOUT_STAT__VIPH_REG_AK); 63209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 64209ff23fSmrg return (INREG(RADEON_VIPH_CONTROL) & 0x2000) ? VIP_BUSY : VIP_RESET; 65209ff23fSmrg } 66209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 67209ff23fSmrg return (INREG(RADEON_VIPH_CONTROL) & 0x2000) ? VIP_BUSY : VIP_IDLE ; 68209ff23fSmrg} 69209ff23fSmrg 70209ff23fSmrgstatic uint32_t RADEONVIP_fifo_idle(GENERIC_BUS_Ptr b, uint8_t channel) 71209ff23fSmrg{ 7268105dcbSveego ScrnInfoPtr pScrn = b->pScrn; 73209ff23fSmrg RADEONInfoPtr info = RADEONPTR(pScrn); 74209ff23fSmrg unsigned char *RADEONMMIO = info->MMIO; 75209ff23fSmrg 76209ff23fSmrg uint32_t timeout; 77209ff23fSmrg 78209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 79209ff23fSmrg timeout = INREG(VIPH_TIMEOUT_STAT); 80209ff23fSmrg if((timeout & 0x0000000f) & channel) /* lockup ?? */ 81209ff23fSmrg { 8268105dcbSveego xf86DrvMsg(b->pScrn->scrnIndex, X_INFO, "RADEON_fifo_idle\n"); 83209ff23fSmrg RADEONWaitForFifo(pScrn, 2); 84209ff23fSmrg OUTREG(VIPH_TIMEOUT_STAT, (timeout & 0xfffffff0) | channel); 85209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 86209ff23fSmrg return (INREG(VIPH_CONTROL) & 0x2000) ? VIP_BUSY : VIP_RESET; 87209ff23fSmrg } 88209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 89209ff23fSmrg return (INREG(VIPH_CONTROL) & 0x2000) ? VIP_BUSY : VIP_IDLE ; 90209ff23fSmrg} 91209ff23fSmrg 92209ff23fSmrg/* address format: 93209ff23fSmrg ((device & 0x3)<<14) | (fifo << 12) | (addr) 94209ff23fSmrg*/ 95209ff23fSmrg 96209ff23fSmrg#define VIP_WAIT_FOR_IDLE() { \ 97209ff23fSmrg int i2ctries = 0; \ 98209ff23fSmrg while (i2ctries < 10) { \ 99209ff23fSmrg status = RADEONVIP_idle(b); \ 100209ff23fSmrg if (status==VIP_BUSY) \ 101209ff23fSmrg { \ 102209ff23fSmrg usleep(1000); \ 103209ff23fSmrg i2ctries++; \ 104209ff23fSmrg } else break; \ 105209ff23fSmrg } \ 106209ff23fSmrg } 107209ff23fSmrg 108209ff23fSmrgstatic Bool RADEONVIP_read(GENERIC_BUS_Ptr b, uint32_t address, uint32_t count, uint8_t *buffer) 109209ff23fSmrg{ 11068105dcbSveego ScrnInfoPtr pScrn = b->pScrn; 111209ff23fSmrg RADEONInfoPtr info = RADEONPTR(pScrn); 112209ff23fSmrg unsigned char *RADEONMMIO = info->MMIO; 113209ff23fSmrg uint32_t status,tmp; 114209ff23fSmrg 115209ff23fSmrg if((count!=1) && (count!=2) && (count!=4)) 116209ff23fSmrg { 117209ff23fSmrg xf86DrvMsg(pScrn->scrnIndex,X_ERROR,"Attempt to access VIP bus with non-stadard transaction length\n"); 118209ff23fSmrg return FALSE; 119209ff23fSmrg } 120209ff23fSmrg 121209ff23fSmrg RADEONWaitForFifo(pScrn, 2); 122209ff23fSmrg OUTREG(RADEON_VIPH_REG_ADDR, address | 0x2000); 123209ff23fSmrg write_mem_barrier(); 124209ff23fSmrg VIP_WAIT_FOR_IDLE(); 125209ff23fSmrg if(VIP_IDLE != status) return FALSE; 126209ff23fSmrg 127209ff23fSmrg/* 128209ff23fSmrg disable RADEON_VIPH_REGR_DIS to enable VIP cycle. 129209ff23fSmrg The LSB of RADEON_VIPH_TIMEOUT_STAT are set to 0 130209ff23fSmrg because 1 would have acknowledged various VIP 131209ff23fSmrg interrupts unexpectedly 132209ff23fSmrg*/ 133209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 134209ff23fSmrg OUTREG(RADEON_VIPH_TIMEOUT_STAT, INREG(RADEON_VIPH_TIMEOUT_STAT) & (0xffffff00 & ~RADEON_VIPH_TIMEOUT_STAT__VIPH_REGR_DIS) ); 135209ff23fSmrg write_mem_barrier(); 136209ff23fSmrg/* 137209ff23fSmrg the value returned here is garbage. The read merely initiates 138209ff23fSmrg a register cycle 139209ff23fSmrg*/ 140209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 141209ff23fSmrg INREG(RADEON_VIPH_REG_DATA); 142209ff23fSmrg 143209ff23fSmrg VIP_WAIT_FOR_IDLE(); 144209ff23fSmrg if(VIP_IDLE != status) return FALSE; 145209ff23fSmrg/* 146209ff23fSmrg set RADEON_VIPH_REGR_DIS so that the read won't take too long. 147209ff23fSmrg*/ 148209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 149209ff23fSmrg tmp=INREG(RADEON_VIPH_TIMEOUT_STAT); 150209ff23fSmrg OUTREG(RADEON_VIPH_TIMEOUT_STAT, (tmp & 0xffffff00) | RADEON_VIPH_TIMEOUT_STAT__VIPH_REGR_DIS); 151209ff23fSmrg write_mem_barrier(); 152209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 153209ff23fSmrg switch(count){ 154209ff23fSmrg case 1: 155209ff23fSmrg *buffer=(uint8_t)(INREG(RADEON_VIPH_REG_DATA) & 0xff); 156209ff23fSmrg break; 157209ff23fSmrg case 2: 158209ff23fSmrg *(uint16_t *)buffer=(uint16_t) (INREG(RADEON_VIPH_REG_DATA) & 0xffff); 159209ff23fSmrg break; 160209ff23fSmrg case 4: 161209ff23fSmrg *(uint32_t *)buffer=(uint32_t) ( INREG(RADEON_VIPH_REG_DATA) & 0xffffffff); 162209ff23fSmrg break; 163209ff23fSmrg } 164209ff23fSmrg VIP_WAIT_FOR_IDLE(); 165209ff23fSmrg if(VIP_IDLE != status) return FALSE; 166209ff23fSmrg /* 167209ff23fSmrg so that reading RADEON_VIPH_REG_DATA would not trigger unnecessary vip cycles. 168209ff23fSmrg*/ 169209ff23fSmrg OUTREG(RADEON_VIPH_TIMEOUT_STAT, (INREG(RADEON_VIPH_TIMEOUT_STAT) & 0xffffff00) | RADEON_VIPH_TIMEOUT_STAT__VIPH_REGR_DIS); 170209ff23fSmrg write_mem_barrier(); 171209ff23fSmrg return TRUE; 172209ff23fSmrg} 173209ff23fSmrg 174209ff23fSmrgstatic Bool RADEONVIP_fifo_read(GENERIC_BUS_Ptr b, uint32_t address, uint32_t count, uint8_t *buffer) 175209ff23fSmrg{ 17668105dcbSveego ScrnInfoPtr pScrn = b->pScrn; 177209ff23fSmrg RADEONInfoPtr info = RADEONPTR(pScrn); 178209ff23fSmrg unsigned char *RADEONMMIO = info->MMIO; 179209ff23fSmrg uint32_t status,tmp; 180209ff23fSmrg 181209ff23fSmrg if(count!=1) 182209ff23fSmrg { 183209ff23fSmrg xf86DrvMsg(pScrn->scrnIndex,X_ERROR,"Attempt to access VIP bus with non-stadard transaction length\n"); 184209ff23fSmrg return FALSE; 185209ff23fSmrg } 186209ff23fSmrg 187209ff23fSmrg RADEONWaitForFifo(pScrn, 2); 188209ff23fSmrg OUTREG(VIPH_REG_ADDR, address | 0x3000); 189209ff23fSmrg write_mem_barrier(); 190209ff23fSmrg while(VIP_BUSY == (status = RADEONVIP_fifo_idle(b, 0xff))); 191209ff23fSmrg if(VIP_IDLE != status) return FALSE; 192209ff23fSmrg 193209ff23fSmrg/* 194209ff23fSmrg disable VIPH_REGR_DIS to enable VIP cycle. 195209ff23fSmrg The LSB of VIPH_TIMEOUT_STAT are set to 0 196209ff23fSmrg because 1 would have acknowledged various VIP 197209ff23fSmrg interrupts unexpectedly 198209ff23fSmrg*/ 199209ff23fSmrg 200209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 201209ff23fSmrg OUTREG(VIPH_TIMEOUT_STAT, INREG(VIPH_TIMEOUT_STAT) & (0xffffff00 & ~VIPH_TIMEOUT_STAT__VIPH_REGR_DIS) ); 202209ff23fSmrg write_mem_barrier(); 203209ff23fSmrg 204209ff23fSmrg/* 205209ff23fSmrg the value returned here is garbage. The read merely initiates 206209ff23fSmrg a register cycle 207209ff23fSmrg*/ 208209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 209209ff23fSmrg INREG(VIPH_REG_DATA); 210209ff23fSmrg 211209ff23fSmrg while(VIP_BUSY == (status = RADEONVIP_fifo_idle(b, 0xff))); 212209ff23fSmrg if(VIP_IDLE != status) return FALSE; 213209ff23fSmrg 214209ff23fSmrg/* 215209ff23fSmrg set VIPH_REGR_DIS so that the read won't take too long. 216209ff23fSmrg*/ 217209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 218209ff23fSmrg tmp=INREG(VIPH_TIMEOUT_STAT); 219209ff23fSmrg OUTREG(VIPH_TIMEOUT_STAT, (tmp & 0xffffff00) | VIPH_TIMEOUT_STAT__VIPH_REGR_DIS); 220209ff23fSmrg write_mem_barrier(); 221209ff23fSmrg 222209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 223209ff23fSmrg switch(count){ 224209ff23fSmrg case 1: 225209ff23fSmrg *buffer=(uint8_t)(INREG(VIPH_REG_DATA) & 0xff); 226209ff23fSmrg break; 227209ff23fSmrg case 2: 228209ff23fSmrg *(uint16_t *)buffer=(uint16_t) (INREG(VIPH_REG_DATA) & 0xffff); 229209ff23fSmrg break; 230209ff23fSmrg case 4: 231209ff23fSmrg *(uint32_t *)buffer=(uint32_t) ( INREG(VIPH_REG_DATA) & 0xffffffff); 232209ff23fSmrg break; 233209ff23fSmrg } 234209ff23fSmrg while(VIP_BUSY == (status = RADEONVIP_fifo_idle(b, 0xff))); 235209ff23fSmrg if(VIP_IDLE != status) return FALSE; 236209ff23fSmrg 237209ff23fSmrg /* 238209ff23fSmrg so that reading VIPH_REG_DATA would not trigger unnecessary vip cycles. 239209ff23fSmrg*/ 240209ff23fSmrg OUTREG(VIPH_TIMEOUT_STAT, (INREG(VIPH_TIMEOUT_STAT) & 0xffffff00) | VIPH_TIMEOUT_STAT__VIPH_REGR_DIS); 241209ff23fSmrg write_mem_barrier(); 242209ff23fSmrg return TRUE; 243209ff23fSmrg 244209ff23fSmrg 245209ff23fSmrg} 246209ff23fSmrg 247209ff23fSmrg 248209ff23fSmrgstatic Bool RADEONVIP_write(GENERIC_BUS_Ptr b, uint32_t address, uint32_t count, uint8_t *buffer) 249209ff23fSmrg{ 25068105dcbSveego ScrnInfoPtr pScrn = b->pScrn; 251209ff23fSmrg RADEONInfoPtr info = RADEONPTR(pScrn); 252209ff23fSmrg unsigned char *RADEONMMIO = info->MMIO; 253209ff23fSmrg 254209ff23fSmrg uint32_t status; 255209ff23fSmrg 256209ff23fSmrg 257209ff23fSmrg if((count!=4)) 258209ff23fSmrg { 259209ff23fSmrg xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Attempt to access VIP bus with non-stadard transaction length\n"); 260209ff23fSmrg return FALSE; 261209ff23fSmrg } 262209ff23fSmrg 263209ff23fSmrg RADEONWaitForFifo(pScrn, 2); 264209ff23fSmrg OUTREG(RADEON_VIPH_REG_ADDR, address & (~0x2000)); 265209ff23fSmrg while(VIP_BUSY == (status = RADEONVIP_idle(b))); 266209ff23fSmrg 267209ff23fSmrg if(VIP_IDLE != status) return FALSE; 268209ff23fSmrg 269209ff23fSmrg RADEONWaitForFifo(pScrn, 2); 270209ff23fSmrg switch(count){ 271209ff23fSmrg case 4: 272209ff23fSmrg OUTREG(RADEON_VIPH_REG_DATA, *(uint32_t *)buffer); 273209ff23fSmrg break; 274209ff23fSmrg } 275209ff23fSmrg write_mem_barrier(); 276209ff23fSmrg while(VIP_BUSY == (status = RADEONVIP_idle(b))); 277209ff23fSmrg if(VIP_IDLE != status) return FALSE; 278209ff23fSmrg return TRUE; 279209ff23fSmrg} 280209ff23fSmrg 281209ff23fSmrgstatic Bool RADEONVIP_fifo_write(GENERIC_BUS_Ptr b, uint32_t address, uint32_t count, uint8_t *buffer) 282209ff23fSmrg{ 28368105dcbSveego ScrnInfoPtr pScrn = b->pScrn; 284209ff23fSmrg RADEONInfoPtr info = RADEONPTR(pScrn); 285209ff23fSmrg unsigned char *RADEONMMIO = info->MMIO; 286209ff23fSmrg 287209ff23fSmrg uint32_t status; 288209ff23fSmrg uint32_t i; 289209ff23fSmrg 290209ff23fSmrg RADEONWaitForFifo(pScrn, 2); 291209ff23fSmrg OUTREG(VIPH_REG_ADDR, (address & (~0x2000)) | 0x1000); 292209ff23fSmrg while(VIP_BUSY == (status = RADEONVIP_fifo_idle(b, 0x0f))); 293209ff23fSmrg 294209ff23fSmrg 295209ff23fSmrg if(VIP_IDLE != status){ 296209ff23fSmrg xf86DrvMsg(pScrn->scrnIndex, X_INFO, "cannot write %x to VIPH_REG_ADDR\n", (unsigned int)address); 297209ff23fSmrg return FALSE; 298209ff23fSmrg } 299209ff23fSmrg 300209ff23fSmrg RADEONWaitForFifo(pScrn, 2); 301209ff23fSmrg for (i = 0; i < count; i+=4) 302209ff23fSmrg { 303209ff23fSmrg OUTREG(VIPH_REG_DATA, *(uint32_t*)(buffer + i)); 304209ff23fSmrg write_mem_barrier(); 305a8a802eaSchristos while(VIP_BUSY == (status = RADEONVIP_fifo_idle(b, 0x0f))) 306a8a802eaSchristos continue; 307209ff23fSmrg if(VIP_IDLE != status) 308209ff23fSmrg { 309209ff23fSmrg xf86DrvMsg(pScrn->scrnIndex, X_INFO, "cannot write to VIPH_REG_DATA\n"); 310209ff23fSmrg return FALSE; 311209ff23fSmrg } 312209ff23fSmrg } 313209ff23fSmrg 314209ff23fSmrg return TRUE; 315209ff23fSmrg} 316209ff23fSmrg 317209ff23fSmrgvoid RADEONVIP_reset(ScrnInfoPtr pScrn, RADEONPortPrivPtr pPriv) 318209ff23fSmrg{ 319209ff23fSmrg RADEONInfoPtr info = RADEONPTR(pScrn); 320209ff23fSmrg unsigned char *RADEONMMIO = info->MMIO; 321209ff23fSmrg 322209ff23fSmrg 323209ff23fSmrg RADEONWaitForIdleMMIO(pScrn); 324209ff23fSmrg switch(info->ChipFamily){ 325209ff23fSmrg case CHIP_FAMILY_RV250: 326209ff23fSmrg case CHIP_FAMILY_RV350: 327209ff23fSmrg case CHIP_FAMILY_R350: 328209ff23fSmrg case CHIP_FAMILY_R300: 329209ff23fSmrg OUTREG(RADEON_VIPH_CONTROL, 0x003F0009); /* slowest, timeout in 16 phases */ 330209ff23fSmrg OUTREG(RADEON_VIPH_TIMEOUT_STAT, (INREG(RADEON_VIPH_TIMEOUT_STAT) & 0xFFFFFF00) | RADEON_VIPH_TIMEOUT_STAT__VIPH_REGR_DIS); 331209ff23fSmrg OUTREG(RADEON_VIPH_DV_LAT, 0x444400FF); /* set timeslice */ 332209ff23fSmrg OUTREG(RADEON_VIPH_BM_CHUNK, 0x0); 333209ff23fSmrg OUTREG(RADEON_TEST_DEBUG_CNTL, INREG(RADEON_TEST_DEBUG_CNTL) & (~RADEON_TEST_DEBUG_CNTL__TEST_DEBUG_OUT_EN)); 334209ff23fSmrg break; 335209ff23fSmrg case CHIP_FAMILY_RV380: 336209ff23fSmrg OUTREG(RADEON_VIPH_CONTROL, 0x003F000D); /* slowest, timeout in 16 phases */ 337209ff23fSmrg OUTREG(RADEON_VIPH_TIMEOUT_STAT, (INREG(RADEON_VIPH_TIMEOUT_STAT) & 0xFFFFFF00) | RADEON_VIPH_TIMEOUT_STAT__VIPH_REGR_DIS); 338209ff23fSmrg OUTREG(RADEON_VIPH_DV_LAT, 0x444400FF); /* set timeslice */ 339209ff23fSmrg OUTREG(RADEON_VIPH_BM_CHUNK, 0x0); 340209ff23fSmrg OUTREG(RADEON_TEST_DEBUG_CNTL, INREG(RADEON_TEST_DEBUG_CNTL) & (~RADEON_TEST_DEBUG_CNTL__TEST_DEBUG_OUT_EN)); 341209ff23fSmrg break; 342209ff23fSmrg default: 343209ff23fSmrg OUTREG(RADEON_VIPH_CONTROL, 0x003F0004); /* slowest, timeout in 16 phases */ 344209ff23fSmrg OUTREG(RADEON_VIPH_TIMEOUT_STAT, (INREG(RADEON_VIPH_TIMEOUT_STAT) & 0xFFFFFF00) | RADEON_VIPH_TIMEOUT_STAT__VIPH_REGR_DIS); 345209ff23fSmrg OUTREG(RADEON_VIPH_DV_LAT, 0x444400FF); /* set timeslice */ 346209ff23fSmrg OUTREG(RADEON_VIPH_BM_CHUNK, 0x151); 347209ff23fSmrg OUTREG(RADEON_TEST_DEBUG_CNTL, INREG(RADEON_TEST_DEBUG_CNTL) & (~RADEON_TEST_DEBUG_CNTL__TEST_DEBUG_OUT_EN)); 348209ff23fSmrg } 349209ff23fSmrg} 350209ff23fSmrg 351209ff23fSmrgvoid RADEONVIP_init(ScrnInfoPtr pScrn, RADEONPortPrivPtr pPriv) 352209ff23fSmrg{ 3532f39173dSmrg pPriv->VIP=calloc(1,sizeof(GENERIC_BUS_Rec)); 35468105dcbSveego pPriv->VIP->pScrn=pScrn; 355209ff23fSmrg pPriv->VIP->DriverPrivate.ptr=pPriv; 356209ff23fSmrg pPriv->VIP->ioctl=RADEONVIP_ioctl; 357209ff23fSmrg pPriv->VIP->read=RADEONVIP_read; 358209ff23fSmrg pPriv->VIP->write=RADEONVIP_write; 359209ff23fSmrg pPriv->VIP->fifo_read=RADEONVIP_fifo_read; 360209ff23fSmrg pPriv->VIP->fifo_write=RADEONVIP_fifo_write; 361209ff23fSmrg 362209ff23fSmrg RADEONVIP_reset(pScrn, pPriv); 363209ff23fSmrg} 364